java - JAVA 中的双重检查锁定

标签 java multithreading thread-safety double-checked-locking

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

// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }

    // other functions and members...
}

现在,我的问题是为什么不删除 volatile来自助手?如果我错了,请纠正我,但如果你只是打破了这条线:helper = result = new Helper();至:

result = new Helper();
helper = result;

在这种情况下 helper在对象完成之前永远不会获得引用。不是这样吗?怎么样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
}

如果删除 volatile ,编译器可以假设没有其他线程使用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
}

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

关于java - JAVA 中的双重检查锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23853705/

相关文章:

java - 使用 Spring 或 AspectJ 将基于代码的样式转换为基于注释的 AOP

java - 有什么关于加速克隆 HashMap 的 Java 代码的建议吗?

java - 访问字符串中的单个字母/数字中的数字 - Java

c++ - 了解使用 std::condition_variable 的示例

java - 同步块(synchronized block)和方法未按预期工作

java - 通过引用延迟初始化的非 volatile 字符串进行访问是线程安全的吗?

java - org.hibernate.Session 和 org.hibernate.classic.Session 之间的区别

multithreading - 使用QTcpServer/QTcpSocket管理来自单个客户端的并发请求

c - 在C中随机时间生成随机线程

c++ - 是否有适用于 C++ 的线程安全图形库?