引述[0]
双检测机制主要用于多线程环境下的延迟初始化,也经常和单件(Singleton)模式在一起使用。如果只讨论Singleton模式,不必这么麻烦。
下面是一段双检测锁代码:
// 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... }
双检测机制不起作用的原因是对象(Helper)的创建和成员(helper)的初始化顺序是不确定的。比如,线程调用方法getHelper(),可以获取一个非空的helper的引用,但是可能发现helper对象成员的值不是正确的应该在构造函数执行之后的值。如果创建对象和初始化对象是一步执行(inline)的,那两者的顺序就无所谓。
在Symantec JIT的虚拟机上的测试验证了双检测的问题。比如下面的语句:
singletons[i].reference = new Singleton();
赋值操作是在构造函数之前执行的。
在Singleton模式下,如果可以只有一个对象,可以定义静态成员。文中给的例子是在一个单独的类中定义:
class HelperSingleton { static Helper singleton = new Helper(); }
class Foo { /** If perThreadInstance.get() returns a non-null value, this thread has done synchronization needed to see initialization of helper */ private final ThreadLocal perThreadInstance = new ThreadLocal(); private Helper helper = null; public Helper getHelper() { if (perThreadInstance.get() == null) createHelper(); return helper; } private final void createHelper() { synchronized(this) { if (helper == null) helper = new Helper(); } // Any non-null value would do as the argument here perThreadInstance.set(perThreadInstance); } }
// Works with acquire/release semantics for volatile // Broken under current semantics for volatile class Foo { private volatile Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) helper = new Helper(); } } return helper; } }