了解 Java 易失性可见性 [英] Understanding Java volatile visibility

查看:33
本文介绍了了解 Java 易失性可见性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读 Java volatile 关键字,但对其可见性"感到困惑.

I'm reading about the Java volatile keyword and have confusion about its 'visibility'.

volatile 关键字的典型用法是:

A typical usage of volatile keyword is:

volatile boolean ready = false;
int value = 0;

void publisher() {
    value = 5;
    ready = true;
}

void subscriber() {
    while (!ready) {}
    System.out.println(value);
}

正如大多数教程所解释的,对 ready 使用 volatile 可确保:

As explained by most tutorials, using volatile for ready makes sure that:

  • 在发布者线程上更改为 ready 对其他线程(订阅者)立即可见;
  • ready 的变化对其他线程可见时,ready 之前的任何变量更新(这里是 value 的变化)是也对其他线程可见;
  • change to ready on publisher thread is immediately visible to other threads (subscriber);
  • when ready's change is visible to other thread, any variable update preceding to ready (here is value's change) is also visible to other threads;

我理解第二点,因为volatile变量通过使用内存屏障来防止内存重新排序,所以在volatile写之前的写入不能在它之后重新排序,而在volatile读之后的读取不能在它之前重新排序.这就是 ready 如何防止在上面的演示中打印 value = 0.

I understand the 2nd, because volatile variable prevents memory reordering by using memory barriers, so writes before volatile write cannot be reordered after it, and reads after volatile read cannot be reordered before it. This is how ready prevents printing value = 0 in the above demo.

但我对第一个保证感到困惑,即 volatile 变量本身的可见性.对我来说,这听起来是一个非常模糊的定义.

But I have confusion about the 1st guarantee, which is visibility of the volatile variable itself. That sounds a very vague definition to me.

换句话说,我的困惑在于单个变量的可见性,而不是多个变量的重新排序或其他什么.让我们简化上面的例子:

In other words, my confusion is just on SINGLE variable's visibility, not multiple variables' reordering or something. Let's simplify the above example:

volatile boolean ready = false;

void publisher() {
    ready = true;
}

void subscriber() {
    while (!ready) {}
}

如果 ready 没有被定义为 volatile,那么订阅者是否有可能无限地卡在 while 循环中?为什么?

If ready is not defined volatile, is it possible that subscriber get stuck infinitely in the while loop? Why?

我想问的几个问题:

  • 立即可见"是什么意思?写操作需要一些时间,那么其他线程多久能看到 volatile 的变化呢?在写入开始后不久但在写入完成之前发生的另一个线程中的读取能否看到更改?
  • 对于现代 CPU 来说,可见性是由缓存一致性协议(例如 MESI)保证的,那么 volatile 在这里有什么帮助?
  • 有些文章说 volatile 变量直接使用内存而不是 CPU 缓存,这保证了线程之间的可见性.这听起来并不正确.
   Time : ---------------------------------------------------------->

 writer : --------- | write | -----------------------
reader1 : ------------- | read | -------------------- can I see the change?
reader2 : --------------------| read | -------------- can I see the change?

希望我清楚地解释了我的问题.

Hope I explained my question clearly.

推荐答案

对于现代 CPU 来说,可见性是由缓存一致性协议(例如 MESI)保证的,那么 volatile 在这里有什么帮助?

Visibility, for modern CPUs is guaranteed by cache coherence protocol (e.g. MESI) anyway, so what can volatile help here?

那对你没有帮助.您不是在为现代 CPU 编写代码,而是在为 Java 虚拟机编写代码,该虚拟机允许具有虚拟 CPU 的虚拟机的虚拟 CPU 缓存不一致.

That doesn't help you. You aren't writing code for a modern CPU, you are writing code for a Java virtual machine that is allowed to have a virtual machine that has a virtual CPU whose virtual CPU caches are not coherent.

有些文章说 volatile 变量直接使用内存而不是 CPU 缓存,这保证了线程之间的可见性.这听起来不太正确.

Some articles say volatile variable uses memory directly instead of CPU cache, which guarantees visibility between threads. That doesn't sound a correct explain.

没错.但是请理解,这与您正在编码的虚拟 机器有关.它的内存很可能在您的物理 CPU 的缓存中实现.这可能允许您的机器使用缓存并仍然具有 Java 规范所需的内存可见性.

That is correct. But understand, that's with respect to the virtual machine that you are coding for. Its memory may well be implemented in your physical CPU's caches. That may allow your machine to use the caches and still have the memory visibility required by the Java specification.

使用 volatile 可以确保写入直接进入虚拟机的内存,而不是虚拟机的虚拟 CPU 缓存.虚拟机的 CPU 缓存不需要提供线程之间的可见性,因为 Java 规范不需要它.

Using volatile may ensure that writes go directly to the virtual machine's memory instead of the virtual machine's virtual CPU cache. The virtual machine's CPU cache does not need to provide visibility between threads because the Java specification doesn't require it to.

您不能假设您的特定物理硬件的特性一定会提供 Java 代码可以直接使用的好处.相反,JVM 会牺牲这些好处来提高性能.但这意味着您的 Java 代码无法获得这些好处.

You cannot assume that characteristics of your particular physical hardware necessarily provide benefits that Java code can use directly. Instead, the JVM trades off those benefits to improve performance. But that means your Java code doesn't get those benefits.

同样,您不是为物理 CPU 编写代码,而是为 JVM 提供的虚拟 CPU 编写代码.您的 CPU 具有一致缓存允许 JVM 执行各种优化以提高您的代码性能,但 JVM 不需要将这些一致缓存传递给您的代码,而真正的 JVM 则不需要.这样做意味着要消除大量极其有价值的优化.

Again, you are not writing code for your physical CPU, you are writing code for the virtual CPU that your JVM provides. That your CPU has coherent caches allows the JVM to do all kinds of optimizations that boost your code's performance, but the JVM is not required to pass those coherent caches through to your code and real JVM's do not. Doing so would mean eliminating a significant number of extremely valuable optimizations.

这篇关于了解 Java 易失性可见性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆