论坛首页 Java企业应用论坛

再请教双重检查锁定DCL的问题

浏览 19864 次
精华帖 (0) :: 良好帖 (11) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-01-21  
在阎宏的《Java与模式》中关于双重检查锁定失效的描述:
public class LazySingleton {
private static LazySingleton m_instance = null;

private LazySingleton() {
}

public static LazySingleton getInstance() {
	if (m_instance == null) {
		synchronized (LazySingleton.class) {
			if (m_instance == null) {
				m_instance = new LazySingleton();
			}
		}
	}
	return m_instance;
}
}

“在Java编译器中,LazySingleton类的初始化与m_instance变量赋值的顺序不可预料。如果一个线程在没有同步化的条件下读取m_instance引用,并调用这个对象的方法的话,可能会发现对象的初始化过程尚未完成,从而造成崩溃。”

可是通过查看编译后的字节码:

......
10 monitorenter
11 getstatic #10 <my/LazySingleton.m_instance>
14 ifnonnull 27 (+13)
17 new #1 <my/LazySingleton>
20 dup
21 invokespecial #20 <my/LazySingleton.<init>>
24 putstatic #10 <my/LazySingleton.m_instance>
27 aload_0
28 monitorexit
......


第21行和22行表明,LazySingleton对象的初始化,是在m_instance变量赋值之前。
至少对于Java编译器,这个次序是明确的,并不是不可预料的。

阎宏给出的理由很容易理解,也更希望是原因是这样的,可实际情况并非如此。
谁能从虚拟机或CPU执行的角度解释一下这个问题吗?

谢谢
   发表时间:2008-01-21  
这个和具体用的jvm有关,不是所有的jvm都能保证这个顺序!
0 请登录后投票
   发表时间:2008-01-21  
这应该和操作系统有关,在linux下线程的执行循序是无序的,好像是这么个意思
0 请登录后投票
   发表时间:2008-01-21  
java 5的spec已经修正了这个问题。不过用volatile的话,jvm的实现还是有些不标准的。

只要LazySingleton的所有instance field都是final的话(immutable),就肯定保证成功。
0 请登录后投票
   发表时间:2008-01-21  
编译后的字节码的顺序是对的,但是jvm执行的时候可能会重排序(reordering),这才是问题所在。这个问题可以通过将m_instance 声明为volatile解决,不过volatile的语义在jdk5以前并没有被正确实现。
0 请登录后投票
   发表时间:2008-01-22  
学习了,以前没注意到。
下面这样是不是也应该没有问题?
public class LazySingleton {
private static LazySingleton m_instance = null;

private LazySingleton() {
}

public static LazySingleton getInstance() {
	if (m_instance == null) {
		synchronized (LazySingleton.class) {
			if (m_instance == null) {
				LazySingleton instance = new LazySingleton();
				m_instance = instance;
			}
		}
	}
	return m_instance;
}
}

0 请登录后投票
   发表时间:2008-01-22  
ls很有趣 都构造器private且 方法也static了,你还在11行
LazySingleton instance = new LazySingleton(); 
0 请登录后投票
   发表时间:2008-01-22  
jomper 写道
ls很有趣 都构造器private且 方法也static了,你还在11行
LazySingleton instance = new LazySingleton(); 


我和楼主写的没有什么区别,只是多个本地变量。。。
虽然构造方法私有,不过是本类中调用的。
虽然是静态方法,不过做实例化也是很正常的。
0 请登录后投票
   发表时间:2008-01-22  
DCL的一个总结,最后一个代码例子比较有意思.
http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl
0 请登录后投票
   发表时间:2008-01-22  
neuzhujf 写道
学习了,以前没注意到。
下面这样是不是也应该没有问题?
public class LazySingleton {
private static LazySingleton m_instance = null;

private LazySingleton() {
}

public static LazySingleton getInstance() {
	if (m_instance == null) {
		synchronized (LazySingleton.class) {
			if (m_instance == null) {
				LazySingleton instance = new LazySingleton();
				m_instance = instance;
			}
		}
	}
	return m_instance;
}
}



说实话,你的代码和lz的没有任何区别,原先有的错误还是存在。
简单的解决方案就是将m_instance声明为volatile,在JDK1.5之后没有问题。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics