首页 > 解决方案 > 当涉及的线程之一是 main() 线程时,为什么线程间可见性不需要 volatile 关键字?

问题描述

考虑以下程序:

import java.util.concurrent.TimeUnit;
public class StopThread {
    public static boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (!stopRequested) {
                    i++;
                    System.out.println(i);
                }
                System.out.println("Stopping the thread!!");
            }
        };
        Thread backgroundThread = new Thread(task);

        backgroundThread.start();
        TimeUnit.SECONDS.sleep(5);
        stopRequested = true;
    }
}

main() 线程对共享变量的所有更新stopRequested对其他线程是否可见?即使不使用volatile关键字?

标签: javamultithreadingconcurrencyvolatile

解决方案


Java 语言规范不保证此结果。

在没有同步操作的情况下(例如volatile写入和后续读取),写入不会发生在读取之前,因此不能保证可见。

也就是说,读取可能会看到旧值,也可能会看到新值;Java 内存模型允许任一结果。

要查看间隙有多窄,请尝试从循环中删除打印:

                while (!stopRequested) {
                    i++;
                }

执行于

openjdk version "14" 2020-03-17
OpenJDK Runtime Environment (build 14+36-1461)
OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)

此代码不会终止。显着的区别是循环体变得不那么复杂,导致 JIT 应用额外的优化:-)

如您所见,不正确同步的程序的行为是不可预测的,并且可能会因最轻微的挑衅而改变。如果你想编写健壮的多线程代码,那么你应该证明你的代码在规范方面是正确的,而不是依赖于测试。


推荐阅读