java - 这是没有 volatile 和同步开销的更好版本的 Double Check Locking

标签 java multithreading jakarta-ee volatile

以下代码片段来自 Effective Java 2nd Edition Double Checked Locking

//Double-check idiom for lazy initialization of instance fields

private volatile FieldType field;

FieldType getField() {
    FieldType result = field;
    if (result == null) {  // First check (no locking)
        synchronized(this) {
            result = field;
            if (result == null)// Second check (with locking)  
                field = result = computeFieldValue();
        }
    }
    return result;
}

据我所知,双重检查锁定的主要问题是在第二次检查锁定中重新排序,这样其他线程可能会看到字段/结果的值已设置,实际上可能仍在执行中。为避免这种情况,我们将字段引用设置为易变的,以保证可见性和重新排序。

但这也可以通过下面的代码实现

private FieldType field; // non volatile
private volatile boolean fence = false;

FieldType getField() {
    if (field == null) {  // First check (no locking) // no volatile read
        synchronized(this) {   //  inside synch block no problem of visibilty will latest           //value  of field 
            if (field == null) {// Second check (with locking)  
                Object obj =  computeFieldValue();
             fence = true; // any volatile write will take. this will make sure statements are //not reorder with setting field as non null.
            field = (FieldType)obj; // this will be only set after computeFieldValue has been //completed fully
           }
        }
    }
    return field;
}

因此在初始化完成后,没有线程需要进行 volatile 读取或同步开销。请 看看我的假设是否正确?

最佳答案

JLS (Section 17.4.5)状态:

"A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field."

您没有阅读 fence更新后的变量,因此更新 fence 的线程之间没有“先发生”关系和任何第二个线程。这意味着不能保证第二个线程看到 field 的更新。第一个线程创建的变量。

简而言之,您的“改进”代码是双重检查锁定的错误实现。

关于java - 这是没有 volatile 和同步开销的更好版本的 Double Check Locking,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17169145/

相关文章:

java - 根据条件跳过 Java 过滤器

jakarta-ee - CDI:异常,因为处置方法看不到生产方法

java - 获取数据库数据到TableView

java - 为什么即使我添加了 try..catch,程序仍然抛出 java.net.ConnectException?

C#.NET 4.0 并发字典 : TryRemove within a lock?

java - 在android中并行下载位图

java - 读取mybatis xml文件时出现"java.net.UnknownHostException"

java - 无法从 Spring Boot 连接到 MySQL 数据库

java - BlockingQueue程序不退出

tomcat - 如何给 web 应用程序起一个全世界都可以访问的名字