JAVA中的双重检查锁定

JAVA中的双重检查锁定,java,multithreading,thread-safety,double-checked-locking,Java,Multithreading,Thread Safety,Double Checked Locking,阅读, 我想知道DCL中的问题和建议的解决方案,或者换句话说,为什么需要volatile关键字? 简而言之,问题在于:在某些情况下,使用DCL可能会导致线程使用部分构造的对象。建议的解决方案(针对JAVA 5+): 现在,我的问题是为什么不从helper中删除volatile?如果我错了,请纠正我,但如果您只是打断这行:helper=result=newhelper()至: result = new Helper(); helper = result; 在这种情况下,助手在对象完成之前将永远不

阅读, 我想知道DCL中的问题和建议的解决方案,或者换句话说,为什么需要
volatile
关键字? 简而言之,问题在于:在某些情况下,使用DCL可能会导致线程使用部分构造的对象。建议的解决方案(针对JAVA 5+):

现在,我的问题是为什么不从helper中删除
volatile
?如果我错了,请纠正我,但如果您只是打断这行:
helper=result=newhelper()至:

result = new Helper();
helper = result;
在这种情况下,
助手在对象完成之前将永远不会获得引用。不是吗?
volatile
如何做得更好

编辑:假设此代码:

class Foo {
    private volatile Helper helper;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    result = new Helper();
                    result.someMethod();
                    helper = result;
                }
            }
        }
        return result;
    }
}

如果初始化后的下一行不能保证一个完全初始化的对象,我就不能对它调用方法。我可以吗?

如果省略
volatile
,编译器可以完全优化
结果。
这个额外的局部变量变化不大。它节省了额外的负载,但修复竞争条件的是
volatile
关键字

假设我们有以下代码片段:

public volatile Object helper;

public synchronized void initHelper() {
    Object result = new Object();
    helper = result;
}
它将被编译为以下伪程序集:

public synchronized void initHelper() {
    Object result = Object.new();
    result.<init>();
    // result is now fully initialized
    // but helper is still null for all threads
    this.helper = result;
    // now all threads can access fully initialized helper
}

如果在
initHelper
中添加函数调用。编译器不会在初始化之前进行此调用。即使在单线程中,这也会改变程序的含义。但是允许它进行优化,这将中断访问此字段的多线程代码。

因为
result=new Helper();助手=结果
helper=result=newhelper()完全相同。您认为会有什么不同?显然,您假设所有操作都将按照与程序顺序一致的总顺序执行。一般来说,多线程程序不是这样。@nosid,我意识到初始化行可能会在初始化完成之前收到地址,但我假设只有在初始化完成后才会执行下一行。否则,在实例化对象之后,我怎么能使用它呢?如果它已完全初始化,我现在就永远无法访问它?请参见编辑。@YekhezkelYovel:单个线程中的操作将始终按程序顺序执行。对于多个线程,如果没有数据争用,则执行将看起来很简单。您的代码在
helper
上包含一个数据竞争,如果变量不是
volatile
@YekhezkelYovel:在第二个列表中,变量
helper
在哪里更新?谢谢@Banthar。它让我更好地理解了
volatile
,尽管我仍然有点困惑。请看我的编辑。谢谢@Banthar。如果我使用
volatile
关键字但不使用局部变量,它会是什么样子?转换不正确,因为
可能会引发异常。在这种情况下,它不会更改任何内容。编译器将自己添加一个临时变量,以便在赋值给字段之前完成初始化<代码>助手
只被分配了一次-编译器不允许更改它。@nosid您是对的。这个例子是一个粗略的简化。也许我不应该称之为
,因为这不是真正的构造函数。
public synchronized void initHelper() {
    Object result = Object.new();
    result.<init>();
    // result is now fully initialized
    // but helper is still null for all threads
    this.helper = result;
    // now all threads can access fully initialized helper
}
public synchronized void initHelper() {
    this.helper = Object.new();
    // from now on other threads can access helper
    this.helper.<init>();
    // but it's fully initialized only after this line
}