java - Java 中 Final 字段和 volatile 字段之间内存模型语义的差异

标签 java multithreading concurrency thread-safety volatile

摘自 Java 并发实践:

When a field is declared volatile, the compiler and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations. Volatile variables are not cached in registers or in caches where they are hidden from other processors, so a read of a volatile variable always returns the most recent write by any thread.(p25)

并且,

Final fields can't be modified(although the objects they refer to can be modified if they are mutable), but they also have special semantics under the Java Memory Model. It is the use of final fields that makes possible the guarantee of initialization safety(see Section 3.5.2) that lets immutable objects be freely accessed and shared without synchronization.(p32)

背诵不安全的出版物:

public class Holder {
      private int n;
      public Holder(int n) { this.n = n; }
      public void assertSanity() {
         if (n != n) // might be true for other threads.
     }
}

令人惊讶的是,n 的值可能会被其他线程视为过时。但 final 修饰符就可以解决问题。类似于 volatile ,不是吗? final 字段本质上是 volatile 吗? (一个可能的解释为什么final volatile是不允许的)

最佳答案

不,final 字段本质上不是 volatile

如果是的话,那就会不必要地昂贵,因为在大多数情况下,您需要在 volatile 写入之后放置一个StoreLoad屏障。

对于 final 字段可以避免这种情况,因为您有一个额外的约束可以帮助您 - 您知道 final 字段必须在相应的类创建时初始化或者实例对象已完全初始化。

该规范可能有点难以阅读(看看 JLS 的 section 17.5),但请记住,就像臭名昭著的 JMM 因果关系部分一样,要点是正式描述直观行为对于大多数人来说。

至于实现,通常需要两件事:

  1. 确保 final 字段存储(包括字段是引用时链下的存储)不能与构造函数外部的存储重新排序。如果底层硬件架构具有强大的内存模型(如 x86),即使您内联构造函数,这通常也是无操作。

  2. 确保给定线程中的第一个 final 字段加载不能与该字段所属的相应引用的同一线程中的第一个加载重新排序。这几乎总是无操作,因为所有编译器和大多数硬件架构都尊重加载依赖性。

最后,大多数架构上便宜得多的 LoadStore 和 StoreStore 屏障应该足以实现 final 字段。

===

您可以阅读有关如何在幕后实现 final 字段的更多信息:

===

附注即使存在 final 字段,不安全的发布也是危险的。请参阅here一些注意事项。

关于java - Java 中 Final 字段和 volatile 字段之间内存模型语义的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36216132/

相关文章:

java - 从 UI 线程调用 GattCallback 中的函数

java正则表达式找到一个字符串,将其添加到数组中,然后替换原来的字符串

c++ - 并发无锁单链表 C++ : does concurrency affect the interface? 迭代器仍然有意义吗?

java - 动态类加载器

python - Flask SQLAlchemy session 不同步

.net - 什么阻止收集 C# 中的线程?

java - Java应用程序中的线程处理

java - Java 中 java.util.concurrent.atomic 包中类的确切用途是什么?

java - 如何在后台线程中启动服务器并知道服务器在启动时没有抛出异常?

java - 变换形状