我正在尝试了解 volatile 关键字及其正确使用方法。看看Brian Goetz的文章Java theory and practice: Fixing the Java Memory Model,我坚持这个例子:
Map configOptions;
char[] configText;
volatile boolean initialized = false;
// In Thread A
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText, configOptions);
initialized = true;
// In Thread B
while (!initialized)
sleep();
// use configOptions
上面的volatile变量用作“guard”,表示已经初始化了一组共享变量。
据我所知,自java 1.5起, volatile 强大到足以确保当线程B读取volatile变量时,它会看到线程A时线程A可见的所有变量写入volatile变量。
但是如果有一个线程C做这样的事情会怎么样:
// In Thread C
configOptions = new HashMap();
// put something to configOptions
我的问题: volatile 强大到足以确保当线程B读取volatile变量时,它会看到来自所有线程的所有变量。也许某种冲洗所有的缓存?如果没有,那么这个带有3个线程的代码就被破坏了,对吗?
答案 0 :(得分:4)
根据lang规范(http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.4):
对易失性变量v(第8.3.1.4节)的写入与任何线程对v的所有后续读取同步(其中“后续”根据同步顺序定义)。
和
在对该字段的每次后续读取之前发生对易失性字段(第8.3.1.4节)的写入。
所以volatile变量本身可以防止过时的缓存问题。你的问题是; “那么所有其他变量呢?”好吧,volatile关键字只影响它所在变量的缓存:这些线程上的所有其他变量都是不同步的。
答案 1 :(得分:0)
在这个答案中,我将尝试解释Java中的 volatile 变量。
那么,从哪里开始?
即使对于64位长度的变量,使用volatile
变量的读写操作也保证是原子的。注意:i++;
不是原子的,因为从技术上讲它是三个变量。
将一些值写入volatile
变量发生 - 之前可以从中读取此值。你可以在之前找到关于发生的事情的很多问题。 重要:在JVM中,它实现了内存栅栏,写入时store
栅栏和读取时load
栅栏。从实际方面来说,这意味着当你从中读取一些值时,你可以保证在volatile写入之前看到写入非易失性变量的所有值;
写入volatile变量的值可立即供所有CPU和所有线程使用,无需任何CPU缓存。
现在,关于你的问题。
volatile的强度是否足以确保当线程B读取volatile变量时,它会看到来自所有线程的所有变量?
没有。它强大到足以确保当线程B从volatile
变量中读取某个值时,它会看到(将读取)来自之前写入的变量的值 volatile write。
也许某种冲洗所有缓存?
实际上是的,在x86架构上,volatile写入清空存储顺序缓冲区,volatile读取清空加载顺序缓冲区。如果您想了解更多详细信息,可能需要阅读此问题的答案:Java 8 Unsafe: xxxFence() instructions
如果没有,那么这个带有3个线程的代码就被破坏了,对吗?
此代码按预期工作(我猜),因为线程B
在读取configOptions
之前执行易失性读取,这保证了它的可见性。
答案 2 :(得分:0)
易失性是否足以确保当线程B读取时 volatile变量,它查看来自所有线程的所有变量。也许一些 有点冲洗所有的缓存?
在易失性存储之前写入的volatile-writing-thread中的所有变量都是可见的。
所以没有'刷新所有缓存'魔法。
如果没有,那么这个带有3个线程的代码就被破坏了,对吗?
如果没有正确同步,很可能会被两个线程破坏。写入初始化标志是有原因的。这有效地刷新了该线程上发生的所有写入。