完美的单例
双重检查锁(DCL)
用处:延迟初始化,降低同步开销。double checked locking
注意:single必须声明为volatile,且支持JDK1.5及以上版本。
对象初始化需要三个步骤:
memory=allocate(); //1.分配内存空间
ctorInstance(memory) //2.初始化对象
instance=memory //3.设置对象指向分配的内存
如果single不是volatile的话,那么处理器会有可能重排序2 3步骤,导致多线程并发时得到实例已经分配内存空间,但并未初始化。
Volatile保证了单例对象的原子性、可见性,禁止了步骤2 3的重排序。
public class Singleton { private volatile static Singleton single; private Singleton(){ } public static Singleton getInstance() { if(single==null){ synchronized(Singleton.class){ if(single==null){ single=new Single(); } } } return single; } }
静态内部类单例
好处:JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。
基于类初始化锁【是类的初始化,并非对象初始化】的特性,可以以静态内部类的方式实现延迟初始化单例:
public class Singleton { private static class SingletonHolder{ public static Singleton single = new Singleton(); } private Singleton(){ } public static Singleton getInstance() { return SingletonHolder.single; } }
初始化一个类,包括执行这个类的静态初始化和初始化这个类中声明的静态字段。根据JAVA规范,当发生如下情况,将初始化一个类:
T是一个类,且T类的一个实例被创建;
T是一个类,且T类中声明的一个静态方法被调用;
T中声明的一个静态字段被赋值;
T中声明的一个静态字段被使用,且这个字段不是一个常量字段;
T是一个顶级类,而且一个断言语句嵌套在T内部被执行。
枚举
枚举是天生的单例。