java - 不变性和重新排序

标签 java multithreading concurrency java-memory-model

下面的代码(Java Concurrency in Practice list 16.3)显然不是线程安全的:

public class UnsafeLazyInitialization {
    private static Resource resource;

    public static Resource getInstance() {
        if (resource == null)
            resource = new Resource();  // unsafe publication
        return resource;
    }
}

然而,几页之后,在第 16.3 节中,他们指出:

UnsafeLazyInitialization is actually safe if Resource is immutable.

我不明白那句话:

  • 如果 Resource 是不可变的,那么任何观察 resource 变量的线程都将看到它为 null 或完全构造(感谢 Java 内存提供的对 final 字段的强大保证型号)
  • 但是,没有什么可以阻止指令重新排序:特别是 resource 的两次读取可以重新排序(if 中的一次读取和 return 中的一次读取)。所以线程可以在 if 条件中看到非空 resource 但返回空引用 (*)。

我认为 UnsafeLazyInitialization.getInstance() 可以返回 null,即使 Resource 是不可变的。是这样吗?为什么(或为什么不是)?


(*) 为了更好地理解我关于重新排序的观点,this blog post杰里米·曼森 (Jeremy Manson) 是 JLS 第 17 章关于并发性的作者之一,他解释了如何通过良性数据竞争安全地发布 String 的哈希码,以及删除局部变量的使用如何导致哈希码错误地返回 0,因为可能的重新排序与我上面描述的非常相似:

What I've done here is to add an additional read: the second read of hash, before the return. As odd as it sounds, and as unlikely as it is to happen, the first read can return the correctly computed hash value, and the second read can return 0! This is allowed under the memory model because the model allows extensive reordering of operations. The second read can actually be moved, in your code, so that your processor does it before the first!

最佳答案

我认为你在这里的困惑是作者所说的安全出版的意思。他指的是非空资源的安全发布,但您似乎明白这一点。

您的问题很有趣 - 是否可以返回 null 缓存的资源值?

是的。

允许编译器像这样重新排序操作

public static Resource getInstance(){
   Resource reordered = resource;
   if(resource != null){
       return reordered;
   }
   return (resource = new Resource());
} 

这不违反顺序一致性规则,但可以返回空值。

这是否是最好的实现还有待商榷,但没有规则可以防止这种类型的重新排序。

关于java - 不变性和重新排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14624365/

相关文章:

c# - 是.GetAwaiter().GetResult();一般用途安全吗?

java - 应用程序退出时如何结束非守护线程?

go - 如何在 go 中修复并发合并排序

c++ - 具有 1 个写入器和 N 个并发读取器的实时数据流

java - Java中信号量的实现

java - 当进度条完成时做某事

java字符串转换

c++ - 卡在 _dl_sysinfo_int80 上的多线程应用程序

java - 为什么在 LinkedHashMap 中通过桶迭代比 HashMap 更快?

java - Elasticsearch : RemoteTransportException in Paginated search for more than 10000 results