Java中的易失性变量

den*_*iss 20 java concurrency multithreading volatile

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

当线程A写入volatile变量并且随后线程B 读取同一个变量时,在读取volatile变量A之前,在写入volatile变量之前可见的所有变量的值都 变为可见B.

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

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

sgo*_*les 25

声明一个易变的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中的非阻塞并发性?


Bre*_*ail 15

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

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

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

  • 正确.Java定义volatile,使得变量的volatile读取具有与synchronized-begin相同的内存效果,并且同一变量的volatile-write具有与synchronized-end相同的内存效果.换句话说,"if(volatile-read){do-work; volatile-write}"就像一个允许多个线程同时执行do-work的同步块. (4认同)
  • 所以你说当一个线程读取一个VOLATILE变量时,它还刷新所有其他变量的缓存? (2认同)

Vin*_*lds 6

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

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

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

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

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

相关问题

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