一尘不染

如何演示Java多线程可见性问题?

java

如果从多个线程访问Java中的变量,则必须确保安全发布它们。这通常意味着使用synchronizedvolatile

我的印象是,我的一些同事没有认真对待这个问题,因为他们“从未听说volatile过,他们的程序已经工作了很多年”。

所以我的问题是:

有人可以提供一个示例Java程序/片段来可靠地显示数据可见性问题。

我认为运行程序并看到意外的NPE或过时的变量值会比理论上的解释更有帮助,这是无法证明的。

非常感谢你的帮助!

更新: 只是再次强调这一点。我已经阅读了 Java Concurreny in Practice, 并了解了 理论上
存在可见性问题的示例。我正在寻找的是一种实际 演示 它们的方法。我不确定这是否确实可行,但是也许有jvm配置或类似的配置允许它。


阅读 251

收藏
2020-09-08

共1个答案

一尘不染

通过删除操作修改此处的示例,我得出一个示例

提出强调使用volatile重要性的最常见示例是while(keepRunning):

public class Test extends Thread {

    boolean keepRunning = true;

    public static void main(String[] args) throws InterruptedException {
        Test t = new Test();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println(System.currentTimeMillis() + ": keepRunning is false");
    }

    public void run() {
        while (keepRunning) 
            System.out.println(System.currentTimeMillis() + ": " + keepRunning);
    }
}

由于keepRunning可以(有效地)保留在运行while循环的线程的高速缓存中,因此在将程序设置为false keepRunning后,该程序可能会长时间打印“ true” keepRunning

但是请注意,没有公开竞争条件的可靠方法。(请参阅我的其他答案。)在某些情况下,此示例可能在硬件/操作系统/ jvm的某些组合中公开。

该示例在我的环境中始终失败(线程永不停止运行)。

// Java environment:
// java version "1.6.0_0"
// OpenJDK Runtime Environment (IcedTea6 1.6.1) (6b16-1.6.1-3ubuntu3)
// OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)
public class Test2 extends Thread {
    boolean keepRunning = true;
    public static void main(String[] args) throws InterruptedException {
        Test2 t = new Test2();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println(System.currentTimeMillis() + ": keepRunning is false");
    }
    public void run() {
        while (keepRunning) 
        {}
    }
}

注意,这类问题完全取决于编译器/运行时/系统。特别是,编译器可以确定添加指令以从内存中读取变量,即使它不是易失性的(这样代码也可以工作),因此vm和jit可以优化从内存中的读取操作,仅使用寄存器,甚至可以使用寄存器。处理器可以对指令重新排序-
不会影响这种情况,但是在其他多线程情况下,如果修改了多个变量,它可能会影响其他线程的感知状态。

2020-09-08