简体   繁体   English

Java中的final和volatile字段在内存模型语义上的差异

[英]Difference in memory model semantics between final and volatile fields in Java

From Java Concurrency In Practice: 从实践中的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时,会通知编译器和运行时该变量是共享的,并且对该变量的操作不应与其他内存操作重新排序。 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) (p25)

And, 和,

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. 最终字段无法修改(尽管它们所引用的对象是可变的,但可以修改),但是它们在Java内存模型下也具有特殊的语义。 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. 使用final字段可以确保初始化安全性(请参阅第3.5.2节),使不可变对象可以自由访问和共享而无需同步。 (p32) (p32)

Reciting an unsafe publication: 引用不安全的出版物:

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.
     }
}

The value of n , surprisingly might be seen stale by other threads. 令人惊讶的是,其他线程可能会看到n的值过时。 But final modifier would do the trick. 但是final修饰符可以解决问题。 Similar to volatile , isn't it? 类似于volatile ,不是吗? Are final fields intrinsically volatile ? final字段本质上是volatile吗? (a possible explanation why final volatile is not allowed) (一个可能的解释,为什么不允许final volatile

No, final fields are not intrinsically volatile . 不, final字段在本质上不是volatile

If they were, that would have been unnecessarily expensive, because in most cases you need to put a StoreLoad barrier after a volatile write. 如果是这样,那将是不必要的昂贵,因为在大多数情况下,您需要在volatile写入之后放置一个StoreLoad屏障。

This can be avoided for final fields, because you have an additional constraint that can help you - you know that final fields must be initialized by the time the corresponding class or instance object is fully initialized. 对于final字段,可以避免这种情况,因为您还有一个附加的约束可以帮助您-您知道必须在相应的类或实例对象完全初始化时对final字段进行初始化。

The specification can be somewhat hard to read (take a look at section 17.5 of the JLS), but keep in mind that, like the notorious JMM Causality section, the main point was to formally describe what would be the intuitive behavior for most people. 该规范可能很难理解(请参阅JLS的17.5节 ),但请记住,就像臭名昭著的JMM因果关系部分一样,要点是正式描述大多数人的直观行为。

As for implementation, it usually requires 2 things: 至于实现,通常需要两件事:

  1. Ensuring that final field stores, including stores down the chain if the field is a reference, cannot be reordered with stores outside of the constructor. 确保final字段存储(包括如果该字段是引用的情况下的链下存储)不能与构造函数外部的存储重新排序。 This is often a no-op, even if you inline your constructor, if the underlying hardware architecture has a strong memory model (like x86). 即使底层的硬件体系结构具有强大的内存模型(例如x86),即使您内联构造函数,这也通常是不操作的。

  2. Ensuring that the first final field load in a given thread cannot be reordered with the first load in that same thread of the corresponding reference to which the field belongs to. 确保给定线程中的第一个final字段加载不能与该字段所属的相应引用的同一线程中的第一个加载重新排序。 This is almost always a no-op, as all compilers and most hardware architectures honor load dependencies. 这几乎总是禁止操作的,因为所有编译器和大多数硬件体系结构都遵循负载依赖性。

In the end, the much less expensive on most architectures LoadStore and StoreStore barriers should be enough for implementing final fields. 最后,在大多数体系结构上,便宜得多的LoadStore和StoreStore壁垒应该足以实现final字段。

=== ===

You can read more about how final fields should be implemented under the covers in: 您可以在以下内容中了解有关如何实现final字段的更多信息:

=== ===

PS Unsafe publication is dangerous even in the presence of final fields. PS不安全出版物即使在存在final字段的情况下也是危险的。 See here for some caveats. 请注意此处的一些警告。

Is it a possible reason private volatile is not allowed? 是否有可能不允许使用私有挥发物?

private volatile is allowed. 允许使用private volatile

Do you mean final volatile ? 您是说final volatile吗? Yes, these modifiers are incompatible by their nature - final var, which value/reference cannot be changed, do not need additional volatile goodies (and associated overhead) because mutation of final field is impossible and reads across multiple threads are consistent 是的,这些修饰符在本质上是不兼容的final var,其值/引用不能更改,不需要其他volatile (以及相关的开销),因为无法更改final字段并且跨多个线程的读取是一致的

But JMM does provide initialisation volatile-style consistancy for final fields. 但是JMM确实为final字段提供了初始化易失性风格的一致性。 AFAIK it was implemented in JSR 133 (included in Java SE 5.0 ). AFAIK在JSR 133 (包含在Java SE 5.0 )中实现。 Before this JSR init reads could be inconsistent during data race (and return null or some intermediate value for example) 在此JSR之前,数据争用期间init读取可能会不一致(例如,返回null或某个中间值)

PS: I've found classic article which mentions your problem. PS:我发现经典文章提到了您的问题。 Highly recommend it (and the second part) 强烈推荐(第二部分)

volatile only has relevance to modifications of the variable itself, not the object it refers to. volatile仅与变量本身的修改有关,与变量所指的对象无关。 It makes no sense to have a final volatile field because final fields cannot be modified. 没有final volatile字段是没有意义的,因为不能修改final字段。 Just declare the field final and it should be fine. 只需声明字段final ,就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM