// Single threaded version
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
helper = new Helper();
return helper;
}
// other functions and members...
}
但在多线程模式下,上述代码会出现错误,于是出现了下面所示的代码:
// Correct 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()是都会进行同步,双重检查机制则试图在变量helper被分配时避免同步。
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null)
synchronized(this) { //1
if (helper == null) //2
helper = new Helper(); //3
}
return helper;
}
// other functions and members...
}
但是上述代码并不能在多线程环境下正确的实现单例,最主要的原因是对上述语句helper=new Helper();,Help对象的初始化和对helper变量赋值的顺序会错乱,这是由于java内存模型导致的。
假设上述代码执行一下时间序列:
1、线程 1 进入 getHelper() 方法。
2、由于 helper为 null,线程 1 在 //1 处进入 synchronized 块。
3、线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非 null。
4、线程 1 被线程 2 预占。
5、线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将 instance 引用返回给一个构造完整但部分初始化了的 Helper对象。
6、线程 2 被线程 1 预占。
7、线程 1 通过运行 Helper 对象的构造函数并将引用返回给它,来完成对该对象的初始化。
为展示此事件的发生情况,假设代码行helper=new Helper(); 执行了下列伪代码:
mem = allocate(); //为单例对象分配内存空间.
instance = mem; //注意,instance 引用现在是非空,但还未初始化
ctorSingleton(instance); //为单例对象通过instance调用构造函数
还有一种利用ThreadLocal来修复双重检查机制的方法:
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);
}
}
这个问题在 J2SE 5.0 中已经被修复,可以使用 volatile 关键字来保证多线程下的单例,但是在之前的版本是无效的,需要注意。
// 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;
}
}
但是
最推荐的完美单例方法是:
public class Something {
private Something() {}
private static class LazyHolder {
private static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
参考文章:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
http://www.iteye.com/topic/652440
http://blog.csdn.net/dl88250/article/details/5439024