Java编译器与双锁检测

环境:

jre5


code


public class LazySingleton2 {

private static LazySingleton2 m_instance = null;

private LazySingleton2() {

}

public static LazySingleton2 getInstance(){

//threads check instance exist or not

if (m_instance == null){

//block threads,which try to create instance

synchronized(LazySingleton2.class){

//allow only one thread enter at a time

if (m_instance == null){

m_instance = new LazySingleton2();

}

}

}

return m_instance;

}//getInstance

}//class


【问题描述】


在上面的下划线处会出现问题。

无法保证编译器为对象分配内存之后,是直接将对象的引用赋值给m_instance,还是将对象初始化之后才将引用赋值给m_instance。如果是前者,其它线程可能获得没有初始化的对象的引用,这将导致严重的问题。


【原因】


产生问题的可能编译方式:伪代码描述

mem = allocate();            //Allocate memory for Singleton object.

instance = mem;              //Note that instance is now non-null, but has not been initialized.other thread may be come at this point.

LazySingleton2<init>(instance);   //Invoke constructor for Singleton passing

此外,因为编译器会进行指令重排序优化,上述伪代码之间可以还会插入其它指令。使得mem所引用对象的初始化操作会延后执行,进而增加了其它线程取得未初始对象的可能性。


【解决方案】


保证jre版本在5.0及以上,并为m_instance 添加volatile关键字。volatile关键字有两个效果:一是对所有线程可见;二是禁止指令重排序优化。


【扩展】


由于类似的原因,下面的这种代码可能会出错。

objectA = objectA + objectA + objectB;

当编译器先分配新对象的空间,然后直接将引用赋值给objectA。那么在之后试图利用原来的objectA引用的对象来初始化新对象时就会出现问题,因为此时objectA已经指向了新分配的对象空间。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章