常见安全的单例实现代码和自己的一点理解。
一、 饿汉模式(静态初始化)
class Singleton{
private Singleton(){
}
private static Singleton cache=new Singleton();
public static Singleton getInstance(){
return Internal.cache;
}
}
实现最为简单,但是如果Singleton的任何一个静态字段(非常量,常量是被动引用,在编译时通过常量传播优化,放入常量池,转化为了对常量池的引用)或者静态方法被调用则会初始化。线程安全有虚拟机的保证,对象的实例化是在类加载的初始化阶段。初始化的时机详见:深入理解java虚拟机p210
二、双重检查锁(dcl)
class Singleton{
private Singleton(){
}
private volatile static Singleton cache;
public static Singleton getInstance(){
if(cache==null){
synchronized (Singleton.class){
if(cache==null){
cache=new Singleton();
}
}
}
return cache;
}
}
双重检查锁是为了解决资源初始化较慢或者资源较重的场景下延迟初始化,利用synchronized关键字保证线程安全。饿汉模式是利用虚拟机本身保证类加载的的线程安全性。但是dcl本身每次都需要检查该对象是否实例化,所以引出下面的模式
三、 延迟占位类
class Singleton{
private Singleton(){
}
private static final class Internal{
static Singleton cache=new Singleton();
}
public static Singleton getInstance(){
return Internal.cache;
}
}
延迟占位类结合前两种的优势,消除了同步并且实现了延迟初始化。延迟占位类模式的线程安全也是由虚拟机来保证的,这里需要理解的是为什么能延迟初始化。相比于饿汉模式,延迟占位类模式可以实现调用getInstance方法才去初始化该类
四、枚举实现
effective java中的推荐做法。我找了一个jdk中的实现如下:
enum NaturalOrderComparator implements Comparator> {
INSTANCE;
@Override
public int compare(Comparable c1, Comparable c2) {
return c1.compareTo(c2);
}
@Override
public Comparator> reversed() {
return Comparator.reverseOrder();
}
}
枚举本身更加安全,虚拟机层面阻止了通过反射去实例化一个类导致单例破坏的场景。
最后
此外还有懒汉模式(线程不安全)和懒汉模式直接加synchronized(性能问题)的,这种代码比较简单,实际也没使用的意义就不展开了。