//Broken multithread version
public class ResourceObject {
private static ResourceObject obj=null;
//unnormal private constructor
private ResourceObject()
{}
//double-check locking method
public static ResourceObject getResourceObject()
{
if(obj == null)//first check position
{
//more than one thread will get here
synchronized(ResourceObject.class)
{
//only one thread every single time here
if(obj == null)//second check here
{
obj=new ResourceObject();
}
}
}
return obj;
}
//other functions and members
}
两次检查看似可以省去不必要的同步开销,但是对于java编译器来说这种技巧并不成立。原因在于,java编译器会优化生成的中间代码,导致 ResourceObject类的初始化与变量obj的赋值之间的顺序变得不可预料。在单线程的情况下,这不会导致程序错误,而且也没有同步问题。但是在 多线程的情况下,在前一个线程执行完赋值语句,然后进行剩下的初始化时,后一个线程就会获得未初始化完的对象实例,此时调用ResourceObject 类的方法,就有可能使用初始化过程中未初始化完的类成员,造成程序崩溃。鉴于这一切行为都是无法预测的,双重检查是不建议在java中使用的。