对volatile类和Atomic类存在一些疑问



我正在阅读Java线程书。我偶然发现

语句1:-"volatile变量只能安全地用于单个加载或存储操作,而不能用于长或双变种。这些限制使得波动性变量的使用变得不常见"

我不明白单次装载或存储操作在这里意味着什么?为什么volatile不能适用于长变体还是双变体?

语句2:-"由于++运算符包含多个指令。AtomicInteger类有一个方法,允许它持有的整数为原子递增。"

为什么易失性整数不能与++运算符一起使用,以及AtomicInteger如何处理它?

声明1:-"挥发性变量只能安全地用于单次加载或存储操作,不能应用于长变量或双变量。这些限制使得挥发性变量的使用变得不常见"

什么?!我认为这完全是错误的。也许你的书已经过时了。

语句2:-"挥发性整数不能与++运算符一起使用,因为++运算符包含多条指令。AtomicInteger类有一个方法,允许它所持有的整数原子递增。"

正是它所说的。++运算符实际上翻译成这样的机器代码(在类似Java的伪代码中):

sync_CPU_caches();
int processorRegister = variable;
processorRegister = processorRegister + 1;
variable = processorRegister;
sync_CPU_caches();

这不是线程安全的,因为即使它有内存屏障,并且以原子方式读取和写入,也不能保证不会在中间有线程开关,并且处理器寄存器是CPU核心的本地寄存器(将它们视为CPU核心中的"本地变量")。但是AtomicInteger是线程安全的——它可能是使用特殊的机器代码指令(如compare和swap)实现的。

volatile变量的主要目的不是立即对该变量进行线程安全访问,而是确保在安全之前发生所谓的

理论上是给打电话

volatile int i = 0;

int i = 0;

没有区别,因为32位字无论如何都是以原子方式编写的(在32位及更高版本的机器上是正确的)。由于指针在内部也是32/64位int,因此volatile基本上只进行一个原子操作,即在32位环境中使用64位长的指针。

然而,发生在之前,这实际上混淆了上面的例子。要理解这一点,您需要知道线程不使用有问题变量的实际内存,但可能会复制它以加快执行速度,并可以重新排序语句以进行优化。现在,如果你有这样的东西:

Thread A: value = 1; doIt = true;
Thread B: if (doIt) { doDoIt(value); }

在线程B中,doIt可能为true,但value还不是1,因为JVM可能已经更改了执行顺序,或者新的value还没有广播到线程B的value的副本。

如果doIt被声明为volatile,那么在访问它的时候,JVM会确保访问之前的所有代码都已执行并广播。所以上面的例子就是使用volatile的实际原因。

相关内容

最新更新