锁定老帖子 主题:再请教双重检查锁定DCL的问题
精华帖 (0) :: 良好帖 (11) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-01-23
http://www.javaworld.com/jw-02-2001/jw-0209-double.html?page=2
这篇文章已经足够清楚了。 tinywind对 double checking 的解释是有问题的。 |
|
返回顶楼 | |
发表时间:2008-01-23
Godlikeme 写道 http://www.javaworld.com/jw-02-2001/jw-0209-double.html?page=2
这篇文章已经足够清楚了。 tinywind对 double checking 的解释是有问题的。 这篇01年的文章,还有阎洪的书,都是java 5以前的。java 5里面理论上volatile是可行的。 |
|
返回顶楼 | |
发表时间:2008-01-23
ajoo 写道 galaxystar 写道 DCL的替代Initialize-On-Demand:
public class Foo { // 似有静态内部类, 只有当有引用时, 该类才会被装载 private static class LazyFoo { public static Foo foo = new Foo(); } public static Foo getInstance() { return LazyFoo.foo; } } 这方法比自己写singleton要好得多,也很简洁。不过一般来说,如果Foo类只有一个getInstance()一个途径得到实例,那么甚至不需要额外的LazyFoo,就直接在Foo类里面private static fiinal Foo singleton = new Foo();就够了。更简单。 DCL更一般的意义上,不是用来做可以用static 变量实现的singleton的(singleton有你提到的这种更好的方法,甚至更好的是不要做singleton,而是用依赖注射),而是在一个instance里面实现延迟加载。 引入LazyFoo是为了initialiazation on demand。这个算是利用java特性的方法,不是一个通用的算法。 |
|
返回顶楼 | |
发表时间:2008-01-23
Godlikeme 写道 http://www.javaworld.com/jw-02-2001/jw-0209-double.html?page=2
这篇文章已经足够清楚了。 tinywind对 double checking 的解释是有问题的。 我认为没有问题,你可以具体指出那一点有问题。 |
|
返回顶楼 | |
发表时间:2008-01-23
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
这个应该是大牛写的,看着比较易懂。 |
|
返回顶楼 | |
发表时间:2008-01-23
jls 12.4.2叙述:
利用jvm静态构造实例,是绝对安全的。 另外,高性能的生产环境, 预先初始化应该优于延迟加载。 |
|
返回顶楼 | |
发表时间:2008-01-23
galaxystar 写道 jls 12.4.2叙述:
利用jvm静态构造实例,是绝对安全的。 另外,高性能的生产环境, 预先初始化应该优于延迟加载。 目前内存这么便宜,还在乎被加载进来吗? 楼上很好,很强大 |
|
返回顶楼 | |
发表时间:2008-01-24
galaxystar 写道 jls 12.4.2叙述:
利用jvm静态构造实例,是绝对安全的。 另外,高性能的生产环境, 预先初始化应该优于延迟加载。 I'm totally agree with you! 1. safe 2. easy Just for me,any other opinions for you? |
|
返回顶楼 | |
发表时间:2008-01-24
dcl的问题,最终还是和java的内存模式有关系,也就是因为java内存模式的原因才使得dcl在c++中可行而java中不可行。
特地仔细的翻了一下《java concurrency in practice》/java 并发编程实践,电子工业出版社有出中文版。在16章java内存模式(这本书翻译为java存储模式)中,专门讲到dcl。 我将内容copy上来吧: The real problem with DCL is the assumption that the worst thing that can happen when reading a shared object reference without synchronization is to erroneously see a stale value (in this case, null); in that case the DCL idiom compensates for this risk by trying again with the lock held. But the worst case is actually considerably worseit is possible to see a current value of the reference but stale values for the object's state, meaning that the object could be seen to be in an invalid or incorrect state. dcl的真正问题在于,它是基于这样的假设:当没有使用同步时读取一个共享变量,可能发生的最坏的事情不过是错误地看到过期值;这种情况下,dcl方法通过占有缩后再检查一次,希望能避免风险。但是,最坏的情况事实上比这还糟糕——线程可能看到引用的当前值(注:指),但是对象的状态值确认过期的。这意味着对象可以被观察到,但是却处于无效或者错误的状态。 下面逐句解释一下: 当没有使用同步时读取一个共享变量 注:指第一个m_instance== null的判断 ,可能发生的最坏的事情不过是错误地看到过期值 注:即其他线程已经修改了m_instance,从默认的null赋值为新建对象的句柄,但是由于没有使用同步,因此当前线程可能无法看到修改后的值 ;这种情况下,dcl方法通过占有锁后再检查一次,希望能避免风险 注:使用synchronized关键字,获取到锁后,再读取m_instance,这个时候读取的值由于有了同步肯定是正确的,因此第二个m_instance == null的判断可以正确进行,之后的创建对象和赋值也是正确的 但是,最坏的情况事实上比这还糟糕——线程可能看到引用的当前值 注:指第一个m_instance== null不成立时,即这个线程看到了赋值后的m_instance引用的值,然后就直接return m_instance了。 ,但是对象的状态值确是过期的。 注:这里翻译的过期有些不好理解,对照英文原文是stale values for the object's state,stale的意思是"不新鲜的, 陈腐的, 疲倦的, 陈旧的". 这意味着对象可以被观察到,但是却处于无效或者错误的状态。 注:这里涉及到一个安全发布的概念,即由于存在重排序的可能性,m_instance = new LazySingleton();中创建对象的过程(即写入对象域)和给m_instance赋值(写如新对象引用)的过程可能发生重排序,出现对象部分创建后就发现m_instance赋值,然后其他线程得到m_instance的值,并通过这个对象访问到还处于部分创建过程中的对象。 在阎宏的《Java与模式》中关于双重检查锁定失效的描述,阐述的是同样的一个道理: “在Java编译器中,LazySingleton类的初始化与m_instance变量赋值的顺序不可预料。如果一个线程在没有同步化的条件下读取m_instance引用,并调用这个对象的方法的话,可能会发现对象的初始化过程尚未完成,从而造成崩溃。” 用一个最简单最容易理解的解释方法(可能不是很正确),就是打如下比方: Object a = new Object(); 假设在Object对象创建过程中需要初始化两个值域m1和m2,正常的过程应该是:开始对象创建,得到一个对象句柄,m1初始化,m2初始化,把这个对象句柄赋值给变量a。 但是由于重排序的存在,可能实际的执行过程变为:开始对象创建,得到一个对象句柄,m1初始化,把这个对象句柄赋值给变量a,m2初始化。而另一个线程在这个对象句柄赋值给变量a后,m2初始化前来访问变量a,并通过a访问到这个创建中的对象,喏,问题出来了,m2初始化还没有完成呢... |
|
返回顶楼 | |
发表时间:2008-01-25
用一个最简单最容易理解的解释方法(可能不是很正确),就是打如下比方:
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(); |
|
返回顶楼 | |