【问题标题】:Java volatile keywordJava volatile 关键字
【发布时间】:2016-02-12 03:29:48
【问题描述】:

我知道有很多关于 volatile 的问题,但我只是从这个讨论中感到困惑: Java: how volatile guarantee visibility of "data" in this piece of code?

我阅读的每个网站都说可以将变量存储在缓存中(使该值对其他线程不可见)我什至发现了这个示例https://dzone.com/articles/java-volatile-keyword-0

所以我的第一个问题是:Java 是否将变量值存储在缓存中(其中一个?l1 l2 或 l3)

我的另一个问题是可见性部分。例如:

public int num1 = 1;
public int num2 = 2;
public int num3 = 3;
public int num4 = 4;
public int num5 = 5;
...
num1 = 10;
num2 = 20;
num3 = 30;
num4 = 40;
num5 = 50;

在这个例子中,变量的执行顺序是没有保证的。 如果我使 num2 易变,它可以向我保证 num1、num2 和 num3 的订单执行将与其定义的完全一样,但它不能向我保证 num4 和 num5 ?

编辑 我刚读完彼得劳里的文章 http://vanillajava.blogspot.com.es/2012/01/demonstrating-when-volatile-is-required.html 他写道:“如果没有 volatile,这会以多种可能的方式发生故障。一种方式是两个线程都认为它们已经更改了值并正在等待另一个,即 每个线程都有自己的缓存值副本. "

所以我更困惑..关于那个

对不起,这个问题可能很愚蠢,但我真的很困惑。

【问题讨论】:

    标签: java multithreading thread-safety


    【解决方案1】:

    当您寻找此类行为的准确描述时,最好查看Java Language Specification。在链接的章节中,您将找到有关线程行为和编译器重新排序以及volatile 如何在此处发挥作用的信息。

    【讨论】:

      【解决方案2】:

      分配的顺序不受volatile 的影响。关键字 volatile 保证多个线程访问例如 int 值“看到”相同的值。这个变量的值永远不会在线程本地缓存:所有读取和写入都将直接进入“主内存”。对变量的访问就像它被包含在一个同步块中一样,在其自身上同步。

      【讨论】:

      • 缓存线不需要写入主存;可以无限期地生活在缓存中。 MESI 协议将确保该值在所有 CPU 的缓存中是一致的。但是 volatile 不会将某些内容从缓存刷新到主内存。 volatile write 的作用是确保在执行下一次加载之前将存储缓冲区排空到 L1D 缓存。
      【解决方案3】:

      包括 JVM 在内的大多数程序都不会明确地将变量放在任何特定的缓存中。 JIT 做出的唯一决定是

      • 它会完全消除变量吗?
      • 它会将其放入寄存器中吗?
      • 是否将它放在内存中的堆栈上? (以及相对于堆栈顶部的位置)
      • 是否将它放在堆上的对象中? (以及对象的位置)

      您唯一的选择是变量是在对象中还是在堆栈中。 (您也可以选择完全避免使用变量)注意,如果您将字段放在对象中,如果可以避免创建对象,jit 仍然可以只将其放入堆栈。

      使num2 volatile 提供的唯一可见性保证是,如果您看到 num2 == 20,则必须看到 num1 == 10。您还可以保证在其他线程中的某个时间点看到 num2 == 20。 您可能会看到 num3 == 3 或 num3 == 30。您可能永远不会在另一个线程中看到 num3,因为它不是 volatile 或 final。如果您在第二次写入 num2 之前在另一个线程中读取该字段,您可能会看到 num3 == 0 这是未初始化的值。

      每个都有自己的缓存副本。

      这不是由 Java 决定的,而是 CPU 的实现细节。每个内核在 x64、Sparc 和 ARM 中都有自己的 L1 和 L2 缓存。这些缓存对于每个内核都是本地的以提高速度,这意味着它们可能彼此不同步。确保它们始终保持同步会大大减慢它们的速度。

      【讨论】:

      • 10 倍于回答彼得。因此,当变量是 volatile 时,保证所有上述变量的顺序与定义的顺序完全相同,但不保证下面的变量。例如,如果我将 num4 定义为 volatile 1,2,3 和 4 将按顺序执行,但从 5 起将无法保证?在这个问题中LinkDavid 说 volatile 与缓存无关。我还是有点困惑。我想我明白 java 与在哪里以及如何......
      • 将存储一个变量。但是为什么在这个问题中说 volatile 与缓存无关?
      • @JDoe 如果它们都是易变的,它们将是有序的。但是,如果只有一个是易变的,那么当您看到它的新值时,您就会看到其他人的先前值。
      • @JDoe 他是对的,volatile 是关于线程之间的可见性,规范没有说明缓存,如果你的 CPU 没有任何缓存,它仍然可以工作。然而,在大多数 CPU(如 x64 和 ARM)中,缓存将参与使这种可见性发生,如所述。
      • (关于订单)我的意思是,如果变量 4 是 volatile 它将在 1,2,3 和 5 之前执行?! (这样对吗?)。 (关于缓存)因此 volatile 变量与缓存无关(缓存是 CPU 问题) volatile 将“要求”获取该变量的最新值(无论来自何处)???