线程安全的单例模式

方案一:Double Check Lock
public class ThreadSafeSingleton {
    private static ThreadSafeSingleton sThreadSafeSingleton;

    public static ThreadSafeSingleton getInstance() {
        if (sThreadSafeSingleton == null) {
            createSingleton();
        }
        return sThreadSafeSingleton;
    }

    private synchronized static void createSingleton() {
        if (sThreadSafeSingleton == null) {
            sThreadSafeSingleton = new ThreadSafeSingleton();
        }
    }

方案二:内部类延迟加载实例化
public class ThreadSafeSingleton {
public static ThreadSafeSingleton getInstance() {
return ThreadSafeSingletonHolder.sThreadSafeSingleton;
}

private static class ThreadSafeSingletonHolder {
private static ThreadSafeSingleton sThreadSafeSingleton = new ThreadSafeSingleton();
}
}

方案三:类初始化时建立静态成员
public class ThreadSafeSingleton {
  private static ThreadSafeSingleton sThreadSafeSingleton = new ThreadSafeSingleton();
public static ThreadSafeSingleton getInstance() {
    return sThreadSafeSingleton;
   }
}

测试代码: public static void main(String[] args) { for (int i = 0; i<3; ++i) { new Thread(new Runnable() { @Override public void run() { TestUtils.print(String.format("thread=%s, singleton=%s", Thread.currentThread().getName(), getInstance())); } }).start(); } }
两个的输出结果是同样的,根据Java Concurrency in practice的理论,方案一存在unsafe publication风险,
即Thread A在初始化sThreadSafeSingleton时,Thread B在读取该变量,因为此处读取操做无锁,
若compile reorder后,sThreadSafeSingleton的赋值操做在构造函数以前,可能会致使Thread B读取了一个不完整的sThreadSafeSingleton对象。
而方案二利用内部类延迟加载(调用getInstance时才初始化内部类,而后实例化对象),所以无需锁操做,性能和安全性上较方案一优。

输出结果为:java

thread=Thread-1, singleton=com.tony.ThreadSafeSingleton@142b7711
thread=Thread-2, singleton=com.tony.ThreadSafeSingleton@142b7711
thread=Thread-0, singleton=com.tony.ThreadSafeSingleton@142b7711安全

 

方案2、三的差异在实例化的时机上,方案三在初始化类时便会实例化(如调用ThreadSafeSingleton静态成员、函数时触发),ide

方案二在初始化内部类时才会触发实例化(主动式引用方式,即调用getInstance,而引用其余静态变量或静态函数时不会触发内部类初始化,函数

从而实现lazy initialization + thread safe instance的效果)。性能

相关文章
相关标签/搜索