// Single threaded version class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { helper = new Helper(); } return helper; } // other functions and members... }
这段在使用多线程的状况下没法正常工做。在多个线程同时调用getHelper()时,必需要获取锁,不然,这些线程可能同时去建立对象,或者某个线程会获得一个未彻底初始化的对象。 php
锁能够经过代价很高的同步来得到,就像下面的例子同样。 html
// Correct but possibly expensive multithreaded version class Foo { private Helper helper = null; public synchronized Helper getHelper() { if (helper == null) { helper = new Helper(); } return helper; } // other functions and members... }
只有getHelper()的第一次调用须要同步建立对象,建立以后getHelper()只是简单的返回成员变量,而这里是无需同步的。 因为同步一个方法会下降100倍或更高的性能[2], 每次调用获取和释放锁的开销彷佛是能够避免的:一旦初始化完成,获取和释放锁就显得很没必要要。许多程序员一下面这种方式进行优化: java
// Broken multithreaded version // "Double-Checked Locking" idiom class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) { helper = new Helper(); } } } return helper; } // other functions and members... }
直觉上,这个算法看起来像是该问题的有效解决方案。然而,这一技术还有许多须要避免的细微问题。例如,考虑下面的事件序列: 程序员
在J2SE 1.4或更早的版本中使用双重检查锁有潜在的危险,有时会正常工做:区分正确实现和有小问题的实现是很困难的。取决于编译器,线程的调度和其余并发系统活动,不正确的实现双重检查锁致使的异常结果可能会间歇性出现。重现异常是十分困难的。 算法
在J2SE 5.0中,这一问题被修正了。volatile关键字保证多个线程能够正确处理单件实例。[4]描述了这一新的语言特性: 编程
// Works with acquire/release semantics for volatile // Broken under Java 1.4 and earlier semantics for volatile class Foo { private volatile Helper helper = null; public Helper getHelper() { Helper result = helper; if (result == null) { synchronized(this) { result = helper; if (result == null) { helper = result = new Helper(); } } } return result; } // other functions and members... }
注意局部变量result的使用看起来是没必要要的。对于某些版本的Java虚拟机,这会使代码提速25%,而对其余的版本则无关痛痒。[3] 缓存
若是helper对象是静态的(每一个类只有一个), 可使用双重检查锁的替代模式惰性初始化模式[4]。查看[5] 上的列表16.6。 安全
// Correct lazy initialization in Java @ThreadSafe class Foo { private static class HelperHolder { public static Helper helper = new Helper(); } public static Helper getHelper() { return HelperHolder.helper; } }
这是由于内部类直到他们被引用时才会加载。 多线程
Java 5中的final语义能够不使用volatile关键字实现安全的建立对象:[6] 并发
public class FinalWrapper<T> { public final T value; public FinalWrapper(T value) { this.value = value; } } public class Foo { private FinalWrapper<Helper> helperWrapper = null; public Helper getHelper() { FinalWrapper<Helper> wrapper = helperWrapper; if (wrapper == null) { synchronized(this) { if (helperWrapper == null) { helperWrapper = new FinalWrapper<Helper>(new Helper()); } wrapper = helperWrapper; } } return wrapper.value; } }