我试图理解volatile
《Java 并发实践》一书中的关键字。我比较synchronized
关键字与volatile
变量在三个方面:原子性、波动性和重新排序。我对此也有一些疑问。我在下面一一讨论过:
1) 可见性:“同步”与“易变”
书上说以下关于可见性synchronized
:
一切线程A
在同步块中或之前执行的操作对于B
当它执行由同一锁保护的同步块时。
它说以下关于可见性volatile
变量:
易失性变量不会缓存在寄存器或缓存中,对其他处理器隐藏,因此对易失性变量的读取始终返回任何线程的最新写入。
易失性变量的可见性影响超出了
易失性变量本身。当线程 A 写入易失性变量并且随后线程 B 读取同一变量时,在写入易失性变量之前对 A 可见的所有变量的值在读取易失性变量后对 B 来说都可见。因此,从内存可见性的角度来看,写入易失性变量就像退出同步块,读取易失性变量就像进入同步块。
Q1.我觉得上面第二段(的volatile
)对应于书上所说的内容synchronized
。但有没有synchronized
-相当于volatile
的第一段?换句话说,是否使用synchronized
确保任何/某些变量不会缓存在处理器缓存和寄存器中?
请注意,这本书还提到了以下关于可见性的内容synchronized
:
锁定不仅仅意味着互斥,还意味着互斥。它还与内存可见性有关。
2)重新排序:“同步”与“易失性”
书上说以下内容volatile
在重新排序的情况下:
当一个字段被声明时volatile
,编译器和运行时会注意到该变量是共享的,并且对其进行的操作不应与其他内存操作重新排序。
Q2.本书没有提及任何有关重新排序的内容synchronized
。有人可以解释一下在以下情况下可以说的重新排序吗synchronized
?
3)原子性
书上说以下关于原子性synchronized
and volatile
.
易失性的语义不够强大,无法进行增量操作(count++
) 原子性,除非你能保证该变量仅从单个线程写入。
加锁可以同时保证可见性和原子性;易失性变量可以
只保证可见性。
Q3.我想这意味着两个线程可以看到volatile int a
两者一起都会增加它然后保存它。但只有最后一次读取才会生效,从而使整个“读取-增量-保存”成为非原子的。我对非原子性的解释是否正确volatile
?
Q4.是否所有等价锁都是可比较的,并且具有相同的可见性、顺序和原子性属性:同步块、原子变量、锁?
PS:这个问题与完全修改的版本有关this https://stackoverflow.com/questions/60931421/lock-vs-synchronized-vs-atomic-variables-vs-volatile-in-java-in-terms-of-read-wr?noredirect=1#comment107849085_60931421几天前我问过的问题。自从全面改版以来,我还没有删除旧的。我以更加集中和结构化的方式写了这个问题。一旦我得到这个问题的答案,就会删除旧的。