我一直在阅读有关volatile(https://www.ibm.com/developerworks/java/library/j-jtp06197/)的内容,并且有些人认为易失性写入比非易失性写入要昂贵得多。
我可以理解,由于volatile是一种同步方式,但是想要知道易失性写入如何比非易失性写入更加昂贵,因此与易失性写入相关的成本会增加;是否可能与进行易失性写入时不同线程堆栈的可见性有关?
答案 0 :(得分:1)
根据您所指出的文章,这就是原因:
易失性写入比非易失性写入要昂贵得多,因为保证可见性所需的内存防护但通常比锁定获取更便宜。
[......]易失性读取很便宜 - 几乎和非易失性读取一样便宜
当然,这是正确的:内存栅栏操作始终绑定到写入,并且无论底层变量是否为volatile,读取都以相同的方式执行。
但是,Java中的volatile
不仅仅是易失性和非易失性内存读取。实际上,它本质上与这种区别无关:区别在于并发语义。
考虑这个臭名昭着的例子:
volatile boolean runningFlag = true;
void run() {
while (runningFlag) { do work; }
}
如果runningFlag
不是volatile
,JIT编译器基本上可以将该代码重写为
void run() {
if (runningFlag) while (true) { do work; }
}
通过在每次迭代中读取runningFlag
而不是完全读取它所引入的开销比例,毋庸置疑,是巨大的。
答案 1 :(得分:0)
关于缓存。由于新处理器使用缓存,如果未指定易失性数据保留在缓存中,则写入操作很快。 (因为缓存靠近处理器)如果变量被标记为易失性,则系统需要将其完全写入内存nad,这是一个稍慢的操作。
是的,你认为它必须用不同的线程堆栈做一些事情,因为每个都是独立的并且从SAME内存读取,但不一定来自同一个缓存。今天处理器使用多级缓存,因此如果多个线程/进程使用相同的数据,这可能是一个大问题。
编辑:如果数据保留在本地缓存中,则其他线程/进程在数据写回内存之前不会看到更改。
答案 2 :(得分:0)
很可能它与易失性写入必须使管道停滞的事实有关。
所有写入都排队等待写入缓存。您没有看到非易失性写入/读取,因为代码只能获取您刚刚编写的值而不涉及缓存。
当你使用易失性读取时,它必须返回缓存,这意味着写入(如已实现)无法继续写入已写入的情况(如果你执行写操作然后读取)
解决此问题的一种方法是使用惰性写入,例如AtomicInteger.lazySet(),它可以比易失性写入快10倍,因为它不会等待。