我感到困惑,volatile 真的有用吗?

3

我写了一个示例来尝试理解volatile。

public class VolatileExample {

private volatile boolean close = false;

public void shutdown() {
    close = true;
}

public void work(){

    Thread t1 = new Thread(new Runnable(){
        public void run(){
            while (!close) {

            }
        }
    });

    Thread t2 = new Thread(new Runnable(){
        public void run(){
            while (!close) {
                shutdown();
            }
        }
    });

    t1.start();
    t2.start();
    try {
        t1.join();
        t2.join();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }   
}

public static void main(String[] args){
    VolatileExample volatileExample = new VolatileExample();
    volatileExample.work();
}   
}

我原本以为程序会停止,但实际上程序没有停止。我将volatile标记从关闭标签中删除后尝试了很多次,我期望程序不会停止,因为线程t1无法看到线程t2对关闭变量所做的更改,但是每次程序都顺利结束。所以我很困惑,既然我们可以在没有使用volatile的情况下完成它,那么volatile有什么用处呢?或者能否给出一个更好的例子来说明何时需要使用volatile和不需要使用volatile的区别?
谢谢!

6
Java不能保证程序不会出错。这是调试并发问题的难点之一。 - user2357112
2个回答

7

内存模型仅表示非易失性字段的更改 可能 不会在其他线程中可见。

也许您的运行时环境比较协作。


1
+1,但要更准确一些,JMM只是说对于volatile字段的更改将在其他线程中可见;它并没有提到非volatile字段(即使这个挑剔也忽略了很多细节)。 - yshavit

2
非易失性字段的更改有时对其他线程可见,有时不可见。它们何时对其他线程可见的时间差异可能会因机器上进行的其他处理、处理器芯片和核心数目、机器上缓存内存的架构等而异数个数量级。
但归根结底,问题在于:有缺陷的并发代码可以成功运行999,999次,但在第一百万次失败。这通常意味着它通过了所有测试,但在真正重要的生产环境中失败了。因此,在编写并发代码时,确保代码正确最好尽力而为 - 即使在测试中似乎没有任何区别,也要使用volatile来访问从多个线程访问的变量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接