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

12 浏览
0 Comments

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

这个问题在这里已经有了答案:

volatile关键字有什么用?

“Volatile”关键字用于什么?

我已经阅读过何时在Java中使用“volatile”?但我仍感到困惑。我该如何知道何时应该将变量标记为volatile?如果我标记错误,无论是在需要时省略了volatile,还是在不需要volatile时加上了它,会发生什么?在多线程代码中解决哪些变量应该是volatile的时候有什么经验法则?

admin 更改状态以发布 2023年5月22日
0
0 Comments

在无锁算法中,volatile是最有用的。当您没有使用锁来访问保存共享数据的变量,并且您希望一个线程所做的更改对另一个线程可见,或者您希望创建一个“happens-after”关系以确保计算不会被重新排序,从而确保更改在适当的时间变得可见时,您可以将该变量标记为volatile。

JMM Cookbook描述了哪些操作可以重新排序,哪些操作不能重新排序。

0
0 Comments

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

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

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

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

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

 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的更改并不一定对主线程“可见”。

基本上这意味着,如果不使用volatile,你的应用程序甚至可能一直写入今天的温度为0,如果你不使用volatile,这种情况是可能发生的(在实践中,该值往往最终变得可见)。但是,当必要时,你不应冒不使用volatile所带来的风险,因为这可能会导致严重的错误(由于“未完全构建的对象”等引起的)。

如果在不需要volatile的地方放置volatile关键字,它不会影响你的代码的正确性(即行为不会改变)。在性能方面,它将取决于JVM实现。理论上,你可能会因为编译器无法重排序优化、必须使CPU缓存失效等而获得微小的性能降级,但是同样的,编译器可以证明你的字段不会被多个线程访问,并完全移除volatile关键字的效果,并编译成相同的指令。

编辑:
回应此评论:

好的,但为什么我们不能将todaysTemperature同步化并创建一个同步的获取temperature的方法?

你可以这样做,它会正确运作。你可以用volatile实现的任何事情都可以用synchronized完成,但反过来不行。有两个原因你可能更喜欢volatile(如果可以的话):

  1. 较少的错误倾向:这取决于上下文,但在许多情况下,使用volatile比使用锁更不容易出现并发错误,如持有锁时阻塞、死锁等。
  2. 更高的性能:在大多数JVM实现中,volatile的吞吐量和延迟可能显着更高。然而,在大多数应用程序中,差异过小而不值得关注。
0