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>形成良好:
每次读取都会在执行中看到对同一变量的写入.易失性变量的所有读写都是易失性动作.对于A中的所有读取r,我们在A和W(r)中有W(r).v = rv当且仅当r是易失性读取时,变量rv是易失性的,并且当且仅当w时,变量wv是易失性的是一个易变的写.
发生在订单之前是部分订单.发生前 - 顺序是通过与边缘和程序顺序同步的传递闭包给出的.它必须是有效的偏序:反身,传递和反对称.
执行遵循线程内一致性.对于每个线程t,由A中的t执行的动作与该线程在隔离的程序顺序中生成的动作相同,每个写入写入值V(w),假设每个读取r看到值V( W(R)).每次读取所见的值由存储器模型确定.给出的程序顺序必须反映根据P的线程内语义执行动作的程序顺序.
执行是在一致之前发生的(§17.4.6).
执行遵循同步顺序一致性.对于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
如果变量是非易失性的,则编译器和CPU可以根据需要自由地重新排序指令,以便优化性能.
如果变量现在声明为volatile,则编译器不再尝试优化对该变量的访问(读取和写入).但是,它可以继续优化其他变量的访问.
在运行时,当访问volatile变量时,JVM会为CPU生成适当的内存屏障指令.内存屏障具有相同的用途 - CPU也可以防止重新排序指令.
当一个volatile变量被写入(通过线程A)时,所有对任何其他变量的写操作都会完成(或者至少看起来是这样)并且在写入volatile变量之前对A可见; 这通常是由于内存写入屏障指令.同样,对其他变量的任何读取都将在读取之前完成(或将显示为)(通过线程B); 这通常是由于内存读屏障指令.由屏障强制执行的指令排序将意味着对A可见的所有写入都将是可见的B.然而,这并不意味着没有发生任何指令的重新排序(编译器可能已执行)重新订购其他指示); 它只是意味着如果A发现任何可见的写入,它就会对B可见.简单来说,这意味着不保持严格的程序顺序.
如果您想更详细地了解JVM如何发布内存屏障指令,我将在内存屏障和JVM并发上指出这篇文章.
相关问题