什么时候在Java中使用volatile关键字? [英] When exactly do you use the volatile keyword in Java?

查看:982
本文介绍了什么时候在Java中使用volatile关键字?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读何时在Java中使用'volatile'?但我我还是很困惑。我怎么知道何时应该标记变量volatile?如果我弄错了,要么在需要它的东西上省略volatile,要么在不可能的东西上放置volatile呢?在确定多线程代码中哪些变量应该是易变的时,有哪些经验法则?

I have read "When to use 'volatile' in Java?" but I'm still confused. How do I know when I should mark a variable volatile? What if I get it wrong, either omitting a volatile on something that needs it or putting volatile on something that doesn't? What are the rules of thumb when figuring out what variables should be volatile in multithreaded code?

推荐答案

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

You basically use it when you want to let a member variable be accessed by multiple threads but do not need compound atomicity (not sure if this is the right terminology).

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++;
    }
}

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

the above is a bad example, because you need compound atomicity.

 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++;
    }
}

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

Now to a valid example:

 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);
        }
    }
}

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

Now, why can't you just use private static int temperature? In fact you can (in the sense that that your program won't blow up or something), but the change to temperature by the other thread may or may not be "visible" to the main thread.

基本上这意味着你的应用甚至可能。如果使用 volatile (在实践中),我们会永远写今天的温度为0 ,该值最终会变得可见。但是,您不应该冒险在必要时不使用volatile,因为它可能导致令人讨厌的错误(由完全构造的对象等引起)。

Basically this means that it is even possible that your app. keeps writing Today's temperature is 0 forever if you don't use volatile (in practice, the value tends to become eventually visible. However, you should not risk not using volatile when necessary, since it can lead to nasty bugs (caused by in-completely constructed objects etc.).

如果你把 volatile 关键字放在不需要 volatile 的东西上,它就不会影响你的代码的正确性(即行为不会改变)。在性能方面,它将取决于JVM的实现。理论上你可能会因为编译器不能重新排序优化,必须使CPU缓存无效等而导致性能下降。但是,然后编译器再次证明您的字段不能被多个线程访问并完全删除 volatile 关键字的影响并将其编译为相同的指令。

If you put volatile keyword on something that doesn't need volatile, it won't affect your code's correctness (i.e. the behaviour will not change). In terms of performance, it will depend on the JVM implementation. In theory you might get a tiny performance degradation because the compiler can't do reordering optimisations, have to invalidate CPU cache etc., but then again the compiler could prove that your field cannot ever be accessed by multiple threads and remove the effect of volatile keyword completely and compile it to identical instructions.

编辑:

回应发表评论:


Response to this comment:


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

Ok, but why can't we make todaysTemperature synchronized and create a synchronized getter for temperature?

你可以,它会表现正常。 volatile 可以使用 synchronized 完成任何操作,但反之亦然。如果可以,有两个原因你可能更喜欢 volatile

You can and it will behave correctly. Anything that you can with volatile can be done with synchronized, but not vice versa. There are two reasons you might prefer volatile if you can:


  1. 减少容易出错:这取决于上下文,但在许多情况下使用 volatile 不太容易出现并发错误,比如在持有锁,死锁等时阻塞。

  2. 性能更高:在大多数JVM实现中, volatile 可以具有更高的吞吐量和更好的延迟。然而,在大多数应用中,差异太小而无关紧要。

  1. Less bug prone: This depends on the context, but in many cases using volatile is less prone to concurrency bugs, like blocking while holding the lock, deadlocks etc.
  2. More performant: In most JVM implementations, volatile can have significantly higher throughput and better latency. However in most applications the difference is too small to matter.

这篇关于什么时候在Java中使用volatile关键字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆