我正在尝试理解《Java Concurrency in Practice》一书中的volatile
关键字。在三个方面(原子性、可见性和重排序)上,我将synchronized
关键字与volatile
变量进行比较。但我对此有些疑虑。以下是我一个一个讨论它们:
1) 可见性: `synchronized` vs `volatile`
关于synchronized
的可见性,该书如下所述:
当一个线程
A
在同步块内或先前执行同步块时,其中的所有操作对于在使用相同锁保护的同步块中执行的B
线程都是可见的。
关于volatile
变量的可见性,它如下所示:
volatile
变量不会被缓存在寄存器或其他处理器不可见的缓存中,因此对volatile
变量的读取总是返回任意线程最新写入的值。
volatile
变量的可见性效果延伸到其本身之外。当线程 A 写入volatile
变量,然后随后线程 B 读取同一变量时,在A写入volatile
变量之前对A可见的所有变量值都将在B读取volatile
变量后对B可见。因此,从内存可见性的角度来看,写入volatile
变量就像退出同步块,而读取volatile
变量就像进入同步块。
Q1. 我认为上面的volatile
第二段对应于书中关于synchronized
的说法。但是是否存在与volatile
第一段等效的synchronized
?换句话说,使用synchronized
是否确保某些变量不被缓存在处理器缓存和寄存器中?
请注意,书中还提到了synchronized
的可见性:
锁定不仅涉及互斥,还涉及内存可见性。
2) 重排序: `synchornized` vs `volatile`
在重排序的上下文中,该书如下所述volatile
:
当一个字段被声明为
volatile
时,编译器和运行时会被通知此变量是共享的,并且对它的操作不应与其他内存操作重新排序。
Q2. 书中没有提到synchronized
的重排序。是否有人可以解释一下在synchronized
的上下文中可能发生的重排序情况?
3) 原子性
该书如下所述关于synchronized
和volatile
的原子性:
如果你不能保证该变量只从一个线程中写入,那么 volatile 的语义不足以使增量操作(
count++
)成为原子操作。锁定可以保证可见性和原子性;volatile 变量只能保证可见性。
Q3. 我猜这意味着两个线程可以同时看到 volatile int a
,都会对其进行递增并保存。但只有最后一次读取将起作用,从而使整个“读取-递增-保存”操作非原子化。我的解释关于 volatile 的非原子性是否正确?
Q4. 所有等效于锁定的方法是否都具有可比性,并具有相同的可见性、排序和原子性属性:同步代码块、原子变量、锁定等?
注:本问题与我几天前提出的 此问题 有关,是完全改版的版本。由于进行了全面改版,我没有删除旧版本。我更加明确和结构化地写下了这个问题。在得到答案之后将删除旧版本。
synchronized (o)
块(其中o
始终引用相同的实例)内访问,则所有线程将始终看到这些变量的一致视图。 - Solomon Slowvolatile
,比如一个线程一直运行直到另一个线程设置了一个volatile bool time_to_stop
标志。否则,在测量了某个重要程序的实际性能,发现它不足以后,并且已经做好了功课以确保使用了最佳算法,现在正在寻求专业技巧来调整它之前,甚至不要想着使用volatile
。 - Solomon Slow