繁体   English   中英

Java中的易失性变量

[英]Volatile variable in Java

所以我正在阅读这本名为Java Concurrency in Practice的书,我坚持这个解释,如果没有一个例子,我似乎无法理解。 这是引用:

当线程A写入易失性变量并且随后线程B读取相同的变量时,在写入易失性变量之前, A可见的所有变量的值在读取volatile变量后变为B可见。

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

我很困惑为什么在读取volatile变量之前所有其他非易失性变量都不会被B看到?

声明一个易变的Java变量意味着:

  • 该变量的值永远不会被线程本地缓存:所有读取和写入将直接进入“主存储器”。
  • 对变量的访问就像包含在同步块中一样,自身同步。

仅供参考,何时需要挥发性?

当多个线程使用相同的变量时,每个线程将拥有自己的该变量的本地缓存副本。 因此,当它更新值时,它实际上在本地缓存中更新,而不是在主变量内存中更新。 使用相同变量的另一个线程对另一个线程更改的值一无所知。 要避免此问题,如果将变量声明为volatile,则它不会存储在本地缓存中。 每当线程更新值时,它都会更新到主存储器。 因此,其他线程可以访问更新的值。

来自JLS§17.4.7 良好的执行

我们只考虑结构良好的执行。 如果满足以下条件,则执行E = <P,A,po,so,W,V,sw,hb>形成良好:

  1. 每次读取都会在执行中看到对同一变量的写入。 易失性变量的所有读写都是易失性动作。 对于A中的所有读取r,我们在A和W(r)中有W(r).v = rv当且仅当r是易失性读取时,变量rv是易失性的,并且当且仅当w时,变量wv是易失性的是一个易变的写。

  2. 发生在订单之前是部分订单。 发生前 - 顺序是通过与边缘和程序顺序同步的传递闭包给出的。 它必须是有效的偏序:反身,传递和反对称。

  3. 执行遵循线程内一致性。 对于每个线程t,由A中的t执行的动作与该线程在隔离的程序顺序中生成的动作相同,每个写入写入值V(w),假设每个读取r看到值V( W(R))。 每次读取所见的值由存储器模型确定。 给出的程序顺序必须反映根据P的线程内语义执行动作的程序顺序。

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

  5. 执行遵循同步顺序一致性。 对于A中的所有易失性读取r,情况并非如此(r,W(r))或存在写入获胜A,使得wv = rv等等(W(r),w)等等( w,r)。

有用的链接: 我们真正了解Java中的非阻塞并发性?

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

有关示例,请阅读以下链接,其中以“使用易失性修复双重检查锁定”结束:

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

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

如果变量现在声明为volatile,则编译器不再尝试优化对该变量的访问(读取和写入)。 但是,可以继续优化其他变量的访问。

在运行时,当访问volatile变量时,JVM会为CPU生成适当的内存屏障指令。 内存屏障具有相同的用途 - CPU也可以防止重新排序指令。

当一个volatile变量写入(通过线程A)时,所有对任何其他变量的写操作都会完成(或者至少会显示),并且在写入volatile变量之前对A可见; 这通常是由于内存写入屏障指令。 同样,对其他变量的任何读取都将在读取之前完成(或将显示为)(通过线程B); 这通常是由于内存读屏障指令。 这种由屏障强制执行的指令排序将意味着对A可见的所有写入都将是可见的B.然而,这并不意味着没有发生任何指令的重新排序(编译器可能已执行)重新订购其他指示); 它只是意味着如果A发现任何可见的写入,它就会对B可见。简单来说,这意味着不保持严格的程序顺序。

如果您想更详细地了解JVM如何发布内存屏障指令,我将在内存屏障和JVM并发上指出这篇文章。

相关问题

  1. 什么是记忆围栏?
  2. 处理器优化代码的一些技巧是什么?

允许线程缓存其他线程自更新以来可能具有的变量值。 volatile关键字强制所有线程不缓存值。

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

通常(即,在没有volatile变量和同步的情况下),VM可以按照它想要的任何顺序将变量从一个线程可见到其他线程,或者根本不存在。 例如,阅读线程可以读取另一个线程变量赋值的早期版本的混合。 这是因为线程可能在具有自己的高速缓存的不同CPU上运行,这些高速缓存有时仅被复制到“主存储器”,并且还通过代码重新排序以用于优化目的。

如果你使用了一个volatile变量,一旦线程B从中读取了一些值X,VM就会确保线程A在写入X之前写入的任何内容对于B也是可见的。(以及A保证为可见的所有内容) ,传递)。

对同步块和其他类型的锁给出了类似的保证。

暂无
暂无

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

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