双重检查锁定模式与最终

问题描述 投票:0回答:1

我正在读这篇文章:https://en.m.wikipedia.org/wiki/Double-checked_locking

Java 中的用法部分,最后一个示例:

Java 5 中 Final 字段的语义可用于安全地发布辅助对象,而无需使用 volatile:

public class FinalWrapper<T> {
    public final T value;
    public FinalWrapper(T value) {
        this.value = value;
    }
}

public class Foo {
   private FinalWrapper<Helper> helperWrapper;

   public Helper getHelper() {
      FinalWrapper<Helper> tempWrapper = helperWrapper;

      if (tempWrapper == null) {
          synchronized (this) {
              if (helperWrapper == null) {
                  helperWrapper = new FinalWrapper<Helper>(new Helper());
              }
              tempWrapper = helperWrapper;
          }
      }
      return tempWrapper.value;
   }
}

为了保证正确性,需要局部变量 tempWrapper:简单地使用 helperWrapper 进行 null 检查,并且 return 语句可能会由于 Java 内存模型允许的读取重新排序而失败。[14]此实现的性能不一定比易失性实现更好。

为什么需要

tempWrapper
才能保证正确性?我不能将其删除并替换为
helperWrapper
吗?据我了解,在构造函数中初始化最终字段
FinalWrapper<Helper>
之前,对
value
创建的对象的引用不会转义到其他线程。如果其他线程将
helperWrapper
读取为非空,则它必须具有正确的
value
值。

java concurrency jvm java-memory-model
1个回答
0
投票

如果你直接在 return 语句中使用 helperWrapper ,它可能看起来像这样:

    public Helper getHelper() {
    if (helperWrapper == null) {
        synchronized (this) {
            if (helperWrapper == null) {
                helperWrapper = new FinalWrapper<Helper>(new Helper());
            }
        }
    }
    return helperWrapper.value; // This could lead to seeing an uninitialized Helper
}

使用局部变量 tempWrapper 可以避免 Java 内存模型下内存可见性和重新排序的潜在问题。它保证一旦分配了 tempWrapper,对完全构造的 Helper 对象的引用始终可以安全使用。这就是为什么它对于正确性至关重要,即使乍一看似乎是多余的。

© www.soinside.com 2019 - 2024. All rights reserved.