意外的线程行为。能见度

时间:2017-02-11 14:18:02

标签: java multithreading

我有以下代码:

public static boolean turn = true;

public static void main(String[] args) {
    Runnable r1 = new Runnable() {
        public void run() {
            while (true) {
                while (turn) {
                    System.out.print("a");
                    turn = false;
                }
            }
        }
    };
    Runnable r2 = new Runnable() {
        public void run() {
            while (true) {
                while (!turn) {
                    System.out.print("b");
                    turn = true;
                }
            }
        }
    };
    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);
    t1.start();
    t2.start();
}

在课堂上我们已经了解了" Visibility"使用未同步的代码时可能出现的问题。 我知道为了节省时间,编译器将决定抢占CPU中的缓存turn,这意味着线程将不知道turn值是否在RAM因为他没有检查它。

根据我的理解,我希望代码能够像这样运行:

T1会看到转为真 - >进入循环并打印 - >改变为假 - >卡住了

T2会认为没有改变 - >会卡住

我希望如果T1将在T2之前开始:只有' a'将打印并且两个线程将在无限循环中运行而不打印任何其他内容

但是,当我运行代码时,我会得到一些" ababa ...."在两个线程都卡住之前。

我错过了什么?

编辑:

以下代码完成了我的预期:线程将以无限循环运行:

public class Test extends Thread {
boolean keepRunning = true;

public void run() {
    long count = 0;
    while (keepRunning) {
        count++;
    }
    System.out.println("Thread terminated." + count);
}

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

}

他们彼此有什么不同?

2 个答案:

答案 0 :(得分:5)

  

当我运行代码时,有时我会得到一些" ababa ...."在两个线程都卡住之前。

我怀疑发生的事情是代码是JIT编译时行为正在发生变化。在JIT编译之前,写入是可见的,因为解释器正在执行直写。在JIT编译之后,缓存刷新或读取已被优化...因为内存模型允许这样做。

  

我错过了什么?

您缺少的是您希望未指定的行为保持一致。它不是必须的。毕竟,它是未指定的! (即使我上面提出的解释不正确,也是如此。)

答案 1 :(得分:1)

turn不易变的事实并不意味着你的代码会破坏,只是它可能会破坏。就我们所知,线程在任何特定时刻都可能看到错误或真实。缓存可以随意刷新,特别是线程可以保留其缓存等等。

可能是因为您的代码遇到了来自System.out.print的副作用,它在内部写入同步方法:

  521       private void write(String s) {
  522           try {
  523               synchronized (this) {

Source - DocJar

synchronized的内存效果可能会刷新缓存,从而影响您的代码。

正如@Stephen C所说,它也可能是JIT,它可能会提升布尔检查,因为它假定该值不能因另一个线程而改变。

因此,在目前为止提到的三种不同的可能性中,它们都可能是影响代码行为的因素。可见性是一个因素,而不是决定因素。