转自:https://my.oschina.net/pingpangkuangmo/blog/376329
一个类只能构造一个对象。
分为懒汉式和饿汉式
饿汉式:
public final class Singleton { private static final Singleton instance=new Singleton();声明时就已构造出对象 private Singleton(){} public static Singleton getInstance(){ return instance; } }
简单粗暴,线程安全。
特点:1.构造器私有,使得别人无法再创建新对象(通过反射方式除外)。
2.提供一个静态方法用于获取对象实例。在类加载器加载Singleton的时候就会去初始化创建一个Singleton实例,类加载器加载Singleton时线程安全的
懒汉式:延迟初始化
public final class Singleton { private volatile static Singleton instance=null;//加volatile 禁止重排序 private Singleton(){} public static Singleton getInstance(){ if(instance==null){ //第一个if,先判断是否为null,减小开销。因为不管有没有初始化都执行sychronized同步,开销很大 synchronized (Singleton.class) { if(instance==null){ //第二个if, 有可能此时已经初始化了,所以要再次判断 instance=new Singleton(); } } } return instance; } }
双重检查机制。看似已经完美,实则不然。instance=new Singleton()实际上分为三个过程:
1 分配内存
2 对Singleton的一些初始化工作包括构造函数的执行
3 对instance变量赋值内存地址
不同的编译器优化时,2和3之间可能会重排序,导致结果:线程1当执行到第2步的时候,instance就已经有值了,此时线程2执行getInstance方法的最外层的if(instance==null)判断就会直接返回。然而该对象还没有真正的完成初始化,还不能正常使用。此时线程2如果去使用该对象,就会出问题了。
具体过程可见:https://my.oschina.net/pingpangkuangmo/blog/376329,一目了然
另一种解决方案:依靠jvm对类和接口的同步来实现单例线程安全的
public final class Singleton { private static class SingletonHolder{ public static Singleton instance=new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.instance; } }
当多个线程执行SingletonHolder.instance时,会首先进行类的初始化,即多个线程可能同时去初始化同一个类,这方面对于jvm来说是进行了细致的同步,每个类都有一个初始化锁,来确保只能有一个线程来初始化类。当线程A获取了SingletonHolder类的初始化锁,线程B则需要等待,线程A就要去执行SingletonHolder的静态变量表达式、静态代码块等初始化工作,然后就能确保Singleton instance=new Singleton()只被一个线程来执行。
总的来说,此种方法是依靠jvm对类和接口的同步来实现单例线程安全的。具体jvm对于类和接口初始化的同步过程可以见这篇文章http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization