什么时候在Java中使用volatile关键字?

Ric*_*ket 85 java multithreading volatile

我读过" 什么时候在Java中使用'volatile'? "但我仍然感到困惑.我怎么知道何时应该标记变量volatile?如果我弄错了,要么在需要它的东西上省略volatile,要么在不需要的东西上放置volatile呢?在确定多线程代码中哪些变量应该是易变的时,有哪些经验法则?

Enn*_*oji 107

当你想让一个成员变量被多个线程访问但不需要复合原子性(不确定这是否是正确的术语)时,你基本上使用它.

class BadExample {
    private volatile int counter;

    public void hit(){
        /* This operation is in fact two operations:
         * 1) int tmp = this.counter;
         * 2) this.counter = tmp + 1;
         * and is thus broken (counter becomes fewer
         * than the accurate amount).
         */
        counter++;
    }
}
Run Code Online (Sandbox Code Playgroud)

以上是一个不好的例子,因为你需要复合原子性.

 class BadExampleFixed {
    private int counter;

    public synchronized void hit(){
        /*
         * Only one thread performs action (1), (2) at a time
         * "atomically", in the sense that other threads can not 
         * observe the intermediate state between (1) and (2).
         * Therefore, the counter will be accurate.
         */
        counter++;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在来看一个有效的例子:

 class GoodExample {
    private static volatile int temperature;

    //Called by some other thread than main
    public static void todaysTemperature(int temp){
        // This operation is a single operation, so you 
        // do not need compound atomicity
        temperature = temp;
    }

    public static void main(String[] args) throws Exception{
        while(true){
           Thread.sleep(2000);
           System.out.println("Today's temperature is "+temperature);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,为什么你不能使用private static int temperature?实际上你可以(在某种意义上说你的程序不会爆炸或者其他东西),但是temperature另一个线程的改变可能对主线程"可见",也可能不是"可见".

基本上这意味着你的应用甚至可能.Today's temperature is 0如果你使用volatile,它会永远写作(在实践中,这个值最终会变得可见.但是,你不应该冒险在必要时不使用volatile,因为它可能导致令人讨厌的错误(由完全构造的对象等造成). ).

如果将volatile关键字放在不需要的东西上volatile,它不会影响代码的正确性(即行为不会改变).在性能方面,它将取决于JVM实现.从理论上讲,由于编译器无法进行重新排序优化,不得不使CPU缓存等无效,因此可能会出现微小的性能下降,但再次编译器可以证明多个线程无法访问您的字段并删除volatile关键字的影响完全并将其编译为相同的指令.

编辑:
回复此评论:

好的,但是为什么我们不能让todaysTemperature同步并为温度创建一个同步的getter?

你可以,它会表现正常.您可以使用的任何东西volatile都可以完成synchronized,但反之亦然.volatile如果可以,您可能会有两个理由:

  1. 减少错误:这取决于上下文,但在许多情况下使用volatile不太容易出现并发错误,例如在持有锁时阻塞,死锁等.
  2. 性能volatile更高:在大多数JVM实现中,可以具有更高的吞吐量和更好的延迟.然而,在大多数应用中,差异太小而不重要.

  • 我已经看过很多这个问题了,这是第一个对我来说很有意义的答案.谢谢. (3认同)
  • @SemyonDanilov:在底部添加了一个答案.HTH. (3认同)
  • 对我来说,目前还不清楚同步吸气器在这里会有什么帮助吗?如果不使用volatile,我们仍然无法保证线程本地缓存. (3认同)
  • @ St.Antario:`synchronized`提供的保证是`volatile`提供的超集,因此除了`synchronized`之外使用`volatile`也是多余的. (3认同)

mdm*_*dma 14

易失性在无锁算法中最有用.当您不使用锁定来访问该变量并且您希望一个线程所做的更改在另一个线程中可见时,或者您希望创建"发生后"关系以确保计算是不再重新订购,以确保在适当的时间可见变化.

JMM食谱介绍了哪些操作可以被重新排序,哪些不能.


Rav*_*abu 6

volatile 关键字保证volatile变量的值总是从主内存中读取,而不是从Thread的本地缓存中读取.

来自java并发教程:

使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任何写入都会建立与之后读取相同变量的先发生关系

这意味着对volatile变量的更改始终对其他线程可见.它还意味着当线程读取volatile变量时,它不仅会看到volatile的最新更改,还会看到导致更改的代码的副作用.

关于你的查询:

我怎么知道何时应该标记变量volatile?在确定多线程代码中哪些变量应该是易变的时,有哪些经验法则?

如果您认为所有读者线程始终获得变量的最新值,则必须将变量标记为 volatile

如果您有一个编写器线程来修改变量和多个读取器线程的值来读取变量的值,那么volatile修饰符可以保证内存的一致性.

如果您有多个线程来编写和读取变量,volatile单独使用修饰符并不能保证内存一致性.你必须synchronize在代码或使用高水平并发结构,如Locks,Concurrent Collections,Atomic variables等.

相关的SE问题/文章:

Java文档中的易失性变量说明

Java中volatile和synchronized之间的区别

javarevisited文章


Joh*_*ter 5

volatile还可用于在多线程环境中安全地发布不可变对象.

声明一个字段可以public volatile ImmutableObject foo确保所有线程始终看到当前可用的实例引用.

有关该主题的更多信息,请参阅实践中的Java并发.

  • 所以我认为这个"不可变"的部分有点值得怀疑......请看我的答案:http://stackoverflow.com/questions/3964317/memory-barriers-and-coding-style-over-a-java -vm和JSR-133 FAQ,项目http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile.我认为更准确的说法是你可以安全地发布不需要其他同步的对象...我可能没有正确理解这一点,但只要发生易失性写入,就会在同一个线程中写入正常的任何阅读线程都可以看到...... (3认同)

归档时间:

查看次数:

41257 次

最近记录:

6 年,5 月 前