论坛首页 Java企业应用论坛

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

浏览 19687 次
精华帖 (0) :: 良好帖 (11) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-01-25  
bluemeteor 写道
galaxystar 写道
jls 12.4.2叙述:
利用jvm静态构造实例,是绝对安全的。

另外,高性能的生产环境, 预先初始化应该优于延迟加载。


I'm totally agree with you!

1. safe
2. easy

Just for me,any other opinions for you?



在同样safe的前提下,easy是很重要的,我也比较偏重与这种实现方法。毕竟现在在spring容器之外自己构造的单例对象并不多,在现在的硬件条件下多搞几个这个算不上消耗
0 请登录后投票
   发表时间:2008-01-25  
呵呵,这里涉及到一个叫做"withinthread as-if-serial semantics"的东东," as long as the program has the same result as if it were executed in program order in a strictly sequential environment, all these games are permissible. "具体请参考《java concurrency in practice》这本书的 16.1. What is a Memory Model, and Why would I Want One? 节。

内容挺不好懂的,我再来一次简单易懂但可能不是很正确的解释吧:
假设要执行的语句是
Object a = getObject();
a.f();
就用上面的例子,重排序后实际的执行过程变为:开始对象创建,得到一个对象句柄,m1初始化,把这个对象句柄赋值给变量a,m2初始化,调用a.f()。这个在单线程环境下是容许的,因为不影响任何执行的结果:即只要最终结果相同(the program has the same result), 中间编辑器/cpu优化怎么搞怎么玩都行(all these games are permissible).

但是如果是多线程就不行了,像上面这样的"玩法",其他线程来访问时就要被玩死了,所以多线程下需要同步.
0 请登录后投票
   发表时间:2008-01-25  
我写了代码,想测出问题,但是没测出来。
谁能帮忙改一下,多谢。我是在JDK1.4.2_16上执行的

package test;

public class Dcl {
	public static void main(String[] args) throws Exception {
		for (int i = 0; i < 100; i ++) {
			Thread t = new Thread(new MyRunable(i));
			t.start();
		}
	}
}

class LazySingleton {
	private static LazySingleton m_instance = null;
	private Test obj1 = null;
	private Test obj2 = null;
	private Test obj3 = null;
	private Test obj4 = null;
	private Test obj5 = null;

	private LazySingleton() {
		obj1 = new Test();
		obj2 = new Test();
		obj3 = new Test();
		obj4 = new Test();
		obj5 = new Test();
	}
	public String toString() {
		return (obj1.toString() + "-" +  obj2.toString() + "-" +  obj3.toString() + "-" +  obj4.toString() + "-" +  obj5.toString());
	}

	public static LazySingleton getInstance() {
		if (m_instance == null) {
			synchronized (LazySingleton.class) {
				if (m_instance == null) {
					m_instance = new LazySingleton();
				}
			}
		}
		return m_instance;
	}
}
class Test {
	public Test() {
		for (int i = 0; i < 100000; i ++) {
			for (int j = 0; j < 10000; j ++) {
				
			}
		}
	}
}
class MyRunable implements Runnable {
	private int i = 0;
	public MyRunable(int index) {
		i = index;
	}
	public void run() {
		System.out.println(i + LazySingleton.getInstance().toString());
	}
}
0 请登录后投票
   发表时间:2008-01-25  
neuzhujf 写道


你上面的解释很容易懂,我也是这么理解的。
但是对下面的执行过程有疑问,能否帮忙解释一下。
下面的getObject方法是否可能在Object创建完成前返回一个无效的对象的引用呢?
Object getObject() {
    return new Object();
}
Object a = getObject();

不会的。在Java5之后,有一个所谓的“happens-before”的概念。基本上,在每一个构造函数结束的地方都有一个freeze动作,在构造函数返回前,所有的final成员变量都要到位。任何指令顺序优化都必须在满足这个限制的前提下进行。所以只要你new的是个immutable对象,Object也好,String也罢,都是安全的。

参见这个文章:
http://www.ibm.com/developerworks/library/j-jtp03304/
5 请登录后投票
   发表时间:2008-01-25  
看看官方的结论吧,http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl,jsr133 faqs对dcl的说明,在1.5之后使用volatile就没有问题了

"In JVMs prior to 1.5, volatile would not ensure that it worked (your mileage may vary). Under the new memory model, making the instance field volatile will "fix" the problems with double-checked locking, because then there will be a happens-before relationship between the initialization of the Something by the constructing thread and the return of its value by the thread that reads it."
0 请登录后投票
   发表时间:2008-01-25  
ajoo 写道
neuzhujf 写道


你上面的解释很容易懂,我也是这么理解的。
但是对下面的执行过程有疑问,能否帮忙解释一下。
下面的getObject方法是否可能在Object创建完成前返回一个无效的对象的引用呢?
Object getObject() {
    return new Object();
}
Object a = getObject();

不会的。在Java5之后,有一个所谓的“happens-before”的概念。基本上,在每一个构造函数结束的地方都有一个freeze动作,在构造函数返回前,所有的final成员变量都要到位。任何指令顺序优化都必须在满足这个限制的前提下进行。所以只要你new的是个immutable对象,Object也好,String也罢,都是安全的。

参见这个文章:
http://www.ibm.com/developerworks/library/j-jtp03304/

如果按照这种说法,前面给出的FinalPlaceHolder没起到作用吧,因为真正需要的是LazySingleton的字段为final。
0 请登录后投票
   发表时间:2008-01-25  
ajoo 写道
neuzhujf 写道


你上面的解释很容易懂,我也是这么理解的。
但是对下面的执行过程有疑问,能否帮忙解释一下。
下面的getObject方法是否可能在Object创建完成前返回一个无效的对象的引用呢?
Object getObject() {
    return new Object();
}
Object a = getObject();

不会的。在Java5之后,有一个所谓的“happens-before”的概念。基本上,在每一个构造函数结束的地方都有一个freeze动作,在构造函数返回前,所有的final成员变量都要到位。任何指令顺序优化都必须在满足这个限制的前提下进行。所以只要你new的是个immutable对象,Object也好,String也罢,都是安全的。

参见这个文章:
http://www.ibm.com/developerworks/library/j-jtp03304/


从你引用的文章来看,似乎这个是对final语义的加强,和dcl的关系不大,在旧的memory model中final变量有可能出现两个值,新的规范避免了这种情况。
0 请登录后投票
   发表时间:2008-01-25  
neuzhujf 写道
用一个最简单最容易理解的解释方法(可能不是很正确),就是打如下比方:
Object a = new Object();
假设在Object对象创建过程中需要初始化两个值域m1和m2,正常的过程应该是:开始对象创建,得到一个对象句柄,m1初始化,m2初始化,把这个对象句柄赋值给变量a。
但是由于重排序的存在,可能实际的执行过程变为:开始对象创建,得到一个对象句柄,m1初始化,把这个对象句柄赋值给变量a,m2初始化。而另一个线程在这个对象句柄赋值给变量a后,m2初始化前来访问变量a,并通过a访问到这个创建中的对象,喏,问题出来了,m2初始化还没有完成呢...


你上面的解释很容易懂,我也是这么理解的。
但是对下面的执行过程有疑问,能否帮忙解释一下。
下面的getObject方法是否可能在Object创建完成前返回一个无效的对象的引用呢?
Object getObject() {
    return new Object();
}
Object a = getObject();


不可能返回一个无效对象引用吧。因为这段代码不存在线程共享变量,没有并发访问的问题。
neuzhujf给出的例子:Object a = new Object();把变量a写成了一个线程私有变量,不太妥。
只有对线程共享变量的赋值才会存在DCL的问题。
0 请登录后投票
   发表时间:2008-01-25  
DCL的问题看一下《java虚拟机规范》第8章“线程和锁”部分,大概可以明白。

不过真搞不懂,这算是Java内存模型的缺陷?还是特意这样设计的?
0 请登录后投票
   发表时间:2008-01-25  
sswh 写道
DCL的问题看一下《java虚拟机规范》第8章“线程和锁”部分,大概可以明白。

不过真搞不懂,这算是Java内存模型的缺陷?还是特意这样设计的?


我想写代码验证存在DCL问题,
然后再验证解决方法对不对。
但是我上面的测试代码测不出问题,
帮忙看看咋回事。
0 请登录后投票
论坛首页 Java企业应用版

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