易失性和更多线程

时间:2014-05-27 19:47:55

标签: java concurrency volatile

我正在尝试了解 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个线程的代码就被破坏了,对吗?

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 变量。

那么,从哪里开始?

  1. 即使对于64位长度的变量,使用volatile变量的读写操作也保证是原子的。注意:i++;不是原子的,因为从技术上讲它是三个变量。

  2. 将一些值写入volatile变量发生 - 之前可以从中读取此值。你可以在之前找到关于发生的事情的很多问题。 重要:在JVM中,它实现了内存栅栏,写入时store栅栏和读取时load栅栏。从实际方面来说,这意味着当你从中读取一些值时,你可以保证在volatile写入之前看到写入非易失性变量的所有值;

  3. 写入volatile变量的值可立即供所有CPU和所有线程使用,无需任何CPU缓存。

  4. 现在,关于你的问题。

      

    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个线程的代码就被破坏了,对吗?

如果没有正确同步,很可能会被两个线程破坏。写入初始化标志是有原因的。这有效地刷新了该线程上发生的所有写入。