Java中的Volatile变量

Java中的Volatile变量,java,multithreading,concurrency,volatile,Java,Multithreading,Concurrency,Volatile,因此,我正在阅读这本名为《实践中的Java并发性》的书,我被困在这一解释上,如果没有一个例子,我似乎无法理解这一解释。以下是报价: 当线程A写入volatile 变量和后续线程B 读取相同的变量,即值 在所有可见的变量中 A在写入volatile之前 变量在B之后变为可见 读取可变变量 有人能给我一个反例,说明为什么“在写入可变变量之前,a可见的所有变量的值在读取可变变量之后,B可见” 我不明白为什么在读取易失性变量之前,所有其他非易失性变量对B不可见?声明易失性Java变量意味着: 这个变量

因此,我正在阅读这本名为《实践中的Java并发性》的书,我被困在这一解释上,如果没有一个例子,我似乎无法理解这一解释。以下是报价:

当线程
A
写入volatile 变量和后续线程
B
读取相同的变量,即值 在所有可见的变量中
A
在写入volatile之前 变量在
B
之后变为可见 读取可变变量

有人能给我一个反例,说明为什么“在写入可变变量之前,
a
可见的所有变量的值在读取可变变量之后,
B
可见”


我不明白为什么在读取易失性变量之前,所有其他非易失性变量对
B
不可见?

声明易失性Java变量意味着:

  • 这个变量的值永远不会被本地线程缓存:所有读写操作都将直接进入“主内存”
  • 对变量的访问就像它被封装在一个同步块中一样,在其自身上是同步的
仅供参考,何时需要volatile

当多个线程使用相同的 变量,每个线程将有其 拥有该服务器的本地缓存的副本 变量所以,当它更新 值,它实际上在 本地缓存不在主变量中 记忆。另一根线是 使用同一个变量并不知道 有关于被更改的值的任何信息吗 这是另一条线。为了避免这种情况 问题是,如果将变量声明为 易失性,则不会存储 在本地缓存中。每当线程 如果正在更新值,它将被更新 到主存储器。那么,其他线程 可以访问更新的值

格式良好的执行

我们只考虑井井有条。 处决。执行E=的形式良好 如果以下条件为真:

  • 每次读取都会看到对同一对象的写入 执行过程中的变量。全部阅读 易变变量的写入是 不稳定的行为。对于所有的读卡器 A、 我们有A中的W(r)和W(r)。v=r.v。 如果且 仅当r是易失性读取,且 变量w.v在且仅当 如果w是易失性写入

  • 发生在订单是部分订单之前 秩序。在下命令之前发生 通过传递闭包 与边和程序同步 秩序。它必须是有效的分部代码 顺序:反身、及物和 反对称的

  • 执行服从 线程内一致性。每人 线程t,由t执行的操作 在一种情况下,我们将是一样的 由中的该线程生成 程序顺序是独立的,每个 在给定值V(w)上写入w 每个readr都能看到值 V(W(r))。每次读取看到的值都是 由记忆模型决定。这个 给出的程序顺序必须反映 程序执行操作的顺序 将根据 P的线程内语义

  • 执行是在一致性之前发生的 (§17.4.6).

  • 执行服从 同步顺序一致性。对于 所有的易失性读数都是r,但不是 (r,W(r))或 存在这样一种“写赢”的方式 w.v=r.v,所以(w(r),w)和 so(w,r)


  • 有用链接:

    线程B可能有这些变量的CPU本地缓存。读取volatile变量可确保观察到从先前写入volatile的任何中间缓存刷新

    例如,阅读以下链接,该链接以“使用Volatile修复双重检查锁定”结尾:


    允许线程缓存其他线程在读取变量值后可能已更新的变量值。
    volatile
    关键字强制所有线程不缓存值。

    如果使用volatile变量,这只是内存模型给您的额外奖励

    通常(即,在没有易失性变量和同步的情况下),VM可以使一个线程中的变量以它想要的任何顺序对其他线程可见,或者根本不可见。例如,读取线程可以读取其他线程变量赋值的早期版本的混合。这是由于线程可能运行在具有自己缓存的不同CPU上,这些缓存有时仅复制到“主内存”,此外,为了优化目的,代码重新排序也会导致这种情况

    如果您使用了一个易失性变量,只要线程B从中读取某个值X,VM就会确保线程a在写入X之前写入的任何内容对B也是可见的(以及保证a是可见的、可传递的)


    对于同步块和其他类型的锁也提供了类似的保证。

    如果变量是非易失性的,那么编译器和CPU可以根据自己的需要自由地重新排序指令,以优化性能

    如果变量现在声明为volatile,则编译器不再尝试优化对该变量的访问(读写)。然而,它可能会继续优化对其他变量的访问

    在运行时,当访问易失性变量时,JVM会向CPU生成适当的内存屏障指令。内存屏障的作用是相同的——CPU还可以防止指令重新排序

    当一个易失性变量被写入(通过线程a)时,对任何其他变量的所有写入都已完成(或至少看起来是这样),并且在写入易失性变量之前对a可见;这通常是由于内存写屏障指令造成的。同样,对其他变量的任何读取都将在 读取(通过线程B);这通常是由于我的错误