如果从多个线程访问Java中的变量,则必须确保安全发布它们。这通常意味着使用synchronized或volatile。
synchronized
volatile
我的印象是,我的一些同事没有认真对待这个问题,因为他们“从未听说volatile过,他们的程序已经工作了很多年”。
所以我的问题是:
有人可以提供一个示例Java程序/片段来可靠地显示数据可见性问题。
我认为运行程序并看到意外的NPE或过时的变量值会比理论上的解释更有帮助,这是无法证明的。
非常感谢你的帮助!
更新: 只是再次强调这一点。我已经阅读了 Java Concurreny in Practice, 并了解了 理论上 存在可见性问题的示例。我正在寻找的是一种实际 演示 它们的方法。我不确定这是否确实可行,但是也许有jvm配置或类似的配置允许它。
通过删除操作修改此处的示例,我得出一个示例
提出强调使用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。
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可以优化从内存中的读取操作,仅使用寄存器,甚至可以使用寄存器。处理器可以对指令重新排序- 不会影响这种情况,但是在其他多线程情况下,如果修改了多个变量,它可能会影响其他线程的感知状态。