论坛首页 Java企业应用论坛

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

浏览 19688 次
精华帖 (0) :: 良好帖 (11) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-01-23  
http://www.javaworld.com/jw-02-2001/jw-0209-double.html?page=2
这篇文章已经足够清楚了。
tinywind对 double checking 的解释是有问题的。
0 请登录后投票
   发表时间: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是可行的。
0 请登录后投票
   发表时间: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特性的方法,不是一个通用的算法。
0 请登录后投票
   发表时间:2008-01-23  
Godlikeme 写道
http://www.javaworld.com/jw-02-2001/jw-0209-double.html?page=2
这篇文章已经足够清楚了。
tinywind对 double checking 的解释是有问题的。


我认为没有问题,你可以具体指出那一点有问题。
0 请登录后投票
   发表时间:2008-01-23  
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
这个应该是大牛写的,看着比较易懂。
0 请登录后投票
   发表时间:2008-01-23  
jls 12.4.2叙述:
利用jvm静态构造实例,是绝对安全的。

另外,高性能的生产环境, 预先初始化应该优于延迟加载。
0 请登录后投票
   发表时间:2008-01-23  
galaxystar 写道
jls 12.4.2叙述:
利用jvm静态构造实例,是绝对安全的。

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

目前内存这么便宜,还在乎被加载进来吗?
楼上很好,很强大
0 请登录后投票
   发表时间: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?
0 请登录后投票
   发表时间: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初始化还没有完成呢...
0 请登录后投票
   发表时间: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();
0 请登录后投票
论坛首页 Java企业应用版

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