Java Notes 00 - Singleton Pattern(单例总结)

转:http://hukai.me/java-notes-singleton-pattern/

这里不赘述单例模式的概念了,直接演示几种不同的实现方式。

0)Eager initialization

如果程序一开始就需要某个单例,并且创建这个单例并不那么费时,我们可以考虑用这种方式:

1
2
3
4
5
6
7
8
9
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

这种实现方式有几个特点:

  • 实例一开始就被创建(Eager initialization)。
  • getInstance()方法不需要加synchronize来解决多线程同步的问题。
  • final关键字确保了实例不可变,并且只会存在一个。

1)Lazy initialization

懒加载的方式使得单例会在第一次使用到时才会被创建.先看一种有隐患的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class LazySingleton {
  private static volatile LazySingleton instance = null;

  // private constructor
  private LazySingleton() {
  }

  public static LazySingleton getInstance() {
      if (instance == null) {
          synchronized (LazySingleton.class) {
              instance = new LazySingleton();
          }
      }
      return instance;
  }
}

请注意:上面的写法其实非线程安全的,假设两个Thread同时进入了getInstance方法,都判断到instance==null,然后会因为synchronized的原因,逐个执行,这样就得到了2个实例。解决这个问题,需要用到典型的double-check方式,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LazySingleton {
    private static volatile LazySingleton instance = null;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton .class) {
                if (instance == null) {
                        instance = new LazySingleton ();
                }
            }
        }
        return instance;
    }
}

另外一个更简略直观的替代写法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LazySingleton {
    private static volatile LazySingleton instance = null;

    private LazySingleton() {
    }

    public static synchronized LazySingleton getInstance() {
            if (instance == null) {
                instance = new LazySingleton ();
            }
        return instance;
    }
}

2)Static block initialization

如果我们对程序的加载顺序有点了解的话,会知道Static block的初始化是执行在加载类之后,Constructor被执行之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class StaticBlockSingleton {
  private static final StaticBlockSingleton INSTANCE;

  static {
      try {
          INSTANCE = new StaticBlockSingleton();
      } catch (Exception e) {
          throw new RuntimeException("Error, You Know This, Haha!", e);
      }
  }

  public static StaticBlockSingleton getInstance() {
      return INSTANCE;
  }

  private StaticBlockSingleton() {
      // ...
  }
}

上面的写法有一个弊端,如果我们类有若干个static的变量,程序的初始化却只需要其中的1,2个的话,我们会做多余的static initialization。

3)Bill Pugh solution

University of Maryland Computer Science researcher Bill Pugh有写过一篇文章initialization on demand holder idiom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() { }

    /**
    * SingletonHolder is loaded on the first execution of Singleton.getInstance()
    * or the first access to SingletonHolder.INSTANCE, not before.
    */
    private static class SingletonHolder {
        public static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

SingletonHolder类会在你需要的时候才会被初始化,而且它不影响Singleton类的其他static成员变量的使用。这个方法是线程安全的并且避免了使用volatile与synchronized。

4)Using Enum

这是最简便安全的方法。没有明显的缺点,并且避免了下面要讲到的序列化的隐患。

1
2
3
4
5
6
public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
        // perform operation here
    }
}

Serialize and de-serialize

在某些情况下,需要实现序列化的时候,普通的单例模式需要添加readResolve的方法,不然会出现异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class DemoSingleton implements Serializable {
  private volatile static DemoSingleton instance = null;

  public static DemoSingleton getInstance() {
      if (instance == null) {
          instance = new DemoSingleton();
      }
      return instance;
  }

  protected Object readResolve() {
      return instance;
  }

  private int i = 10;

  public int getI() {
      return i;
  }

  public void setI(int i) {
      this.i = i;
  }
}

仅仅有上面的还不够,我们需要添加serialVersionUID,例子详见下面的总结。

Conclusion

实现一个功能完善,性能更佳,不存在序列化等问题的单例,建议使用下面两个方式之一:

Bill Pugh(Inner Holder)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DemoSingleton implements Serializable {
  private static final long serialVersionUID = 1L;

  private DemoSingleton() {
      // private constructor
  }

  private static class DemoSingletonHolder {
      public static final DemoSingleton INSTANCE = new DemoSingleton();
  }

  public static DemoSingleton getInstance() {
      return DemoSingletonHolder.INSTANCE;
  }

  protected Object readResolve() {
      return getInstance();
  }
}

Enum

1
2
3
4
5
6
public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
        // perform operation here
    }
}


参考资料

时间: 2024-08-24 04:11:25

Java Notes 00 - Singleton Pattern(单例总结)的相关文章

java中的四种单例实现方式

在java中,单例设计模式是非常常见的设计模式,对单例设计模式的概念,不做过多的介绍,下面将逐一介绍四种单例设计模式: 1.第一种单例设计模式 1.1 代码实现 package com.singleton.one; /**  * 第一种单例设计模式  * @author Administrator  *  */ public class SingleTonOne { // 实例化 private static SingleTonOne instance = new SingleTonOne();

Java设计模式透析之 —— 单例(Singleton)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8860649 写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上线后还可以帮助你分析数据.但是Java原生带有的System.out.println()方法却很少在真正的项目开发中使用,甚至像findbugs等代码检查工具还会认为使用System.out.println()是一个bug. 为什么作为Java新手神器的System.out.println(),到了

Singleton(单例)模式和Double-Checked Locking(双重检查锁定)模式

问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进行写日志.那么,如何构造一个日志模块的实例呢?难道,每次new一个日志模块实例,写完日志,再delete,不要告诉我你是这么干的.在C++中,可以构造一个日志模块的全局变量,那么在任何地方就都可以用了,是的,不错.但是,我所在的开发部门的C++编码规范是参照Google的编码规范的. 全局变量在项目

黑马程序员--Java基础学习笔记【单例设计模式、网络编程、反射】

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 设计模式分类: 创建模式:是对类的实例化过程的抽象化,又分为类创建模式和对象创建模式 类创建模式:工厂方法模式 对象-:简单工厂(静态工厂方法)模式.抽象工厂模式.单例模式.建造模式- 结构模式:描述如何将类或者对象结合在一起形成更大的结构 适配器模式.缺省模式.合成模式.装饰模式(包装模式).门面模式- 行为模式:对不同的对象之间划分责任和算法的抽象化 不变模式.策略模式.迭代子模式.命令模

java 核心学习笔记(四) 单例类

如果一个类始终只能创建一个实例,那么这个类被称作单例类. 一些特殊的应用场景可能会用到,为了保证只能创建一个实例,需要将构造方法用private修饰,不允许在类之外的其它地方创建类的实例. 又要保证可以创建该类的实例,所以需要一个public方法来创建该类的实例,由于在创建之前并没有该类的对象存在,所以只能使用类来调用此方法,故用static修饰. 同时需要将该类创建得到的实例分配一个引用才能知道该实例是否存在与是否唯一,所以需要一个成员变量保存创建的对象,同时由于该对象由上述静态方法创建,则该

java设计模式(1)-------单例,工厂,值对象,装饰模式

      java设计模式(1) 先简单的介绍下设计模式吧:是一种解决问题的一种行之有效的思想:用于解决特定环境下.重复出现的特定问题的解决方案. 那为什么我们需要学习设计模式呢? 1.设计模式都是一些相对优秀的解决方案,很多问题都是典型的.有代表性的问题,学习设计模式,我们就不用自己从头来解决这些问题,相当于在巨人的肩膀上,复用这些方案即可,站的高看到远,就是要站在巨人的肩膀上把他们踩下去,嘿嘿. 2.设计模式已经成为专业人士的常用词汇,不懂不利于交流,能让你变得很牛逼哦. 3.能让你设计的

23种设计模式式之Singleton(单例设计模)【学习笔记】

单例模式是设计模式中最简单,他要求设计者保证该类只有一个实例,之前我是挂载脚本实现单例, 该方法非常简单,使用的直接调用Singleton.instance就可以了 using UnityEngine; using System.Collections; public class Singleton : MonoBehaviour { public static Singleton instance; void Awake(){ instance = this; } } 但是这种写法并不规范,因

java几种方式实现单例设计模式

单例模式的几种实现方式: 一:饿汉式单例 方式一:枚举方式获得单例对象 方式二:静态属性获得单例对象 方式三:静态方法获得单例对象 二:懒汉式单例 方式一:静态方法获得单例对象(线程安全) 方式二:内部类方式去获取单例对象 示例: 恶汉式:方式一 enum Singleton{ INSTANCE;//单例 } 恶汉式:方式二 class Singleton{ public static final Singleton INSTANCE = new Singleton();//单例 private

IOS Singleton(单例)

Singleton.h // .h #define singleton_interface(class) + (instancetype)shared##class; // .m #define singleton_implementation(class) static class *_instance; + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&o