简体   繁体   English

意外的线程行为。 能见度

[英]Unexpected thread behavior. Visibility

I have the following code: 我有以下代码:

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();
}

In class we've learned about "Visibility" problems that may occur when using un-synchronized code. 在课堂上,我们了解了使用非同步代码时可能发生的“可见性”问题。 I understand that in order to save time, the compiler will decide the grab turn to the cache in the CPU for the loop, meaning that the thread will not be aware if the turn value was changed in the RAM because he doesn't check it. 我知道,为了节省时间,编译器将为循环决定对CPU缓存的抓取turn ,这意味着线程不会知道RAM中的turn值是否已更改,因为他不检查它。

From what I understand, I would expected the code to run like this: 据我了解,我希望代码像这样运行:

T1 will see turn as true -> enter loop and print -> change turn to false -> gets stuck T1将看到转为true->进入循环并打印->将转为false->被卡住

T2 will think turn hasn't changed -> will get stuck T2会认为转弯没有改变->会被卡住

I would expect that if T1 will start before T2: only 'a' will be printed and both threads will run in an infinite loop without printing anything else 我希望如果T1在T2之前开始:只会打印'a',并且两个线程都将在无限循环中运行而无需打印其他任何内容

However, when I'm running the code sometimes I get a few "ababa...." before both threads will stuck. 但是,当我运行代码时,有时我会在两个线程卡住之前得到一些“ abab ....”。

What am I missing ? 我想念什么?

EDIT: 编辑:

The following code does what I expect it: the thread will run in a infinite loop: 以下代码完成了我所期望的:线程将在无限循环中运行:

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.");
}

} }

How are they different from each other ? 它们彼此之间有何不同?

When I run the code, sometimes I get a few "ababa...." before both threads will stuck. 当我运行代码时,有时我会在两个线程卡住之前得到一些“ abab ....”。

I suspect that what is happening is that the behavior is changing when the code is JIT compiled. 我怀疑正在发生的事情是,在对代码进行JIT编译时,行为正在改变。 Before JIT compilation the writes are visible because the interpreter is doing write-throughs. 在JIT编译之前,写操作是可见的,因为解释器正在执行写操作。 After JIT compilation, either the cache flushes or the reads have been optimized away ... because the memory model allows this. 在JIT编译之后,缓存刷新或读取已被优化……因为内存模型允许这样做。

What am I missing ? 我想念什么?

What you are missing is that you are expecting unspecified behavior to be consistent. 您缺少的是您期望未指定的行为是一致的。 It doesn't have to be. 不一定是。 After all, it is unspecified! 毕竟,它是未指定的! (This is true, even if my proposed explanation above is incorrect.) (这是正确的,即使我上面提出的解释不正确。)

The fact that turn isn't volatile doesn't mean that your code WILL break, just that it MIGHT break. turn不是不稳定的事实并不意味着您的代码会中断,而可能会中断。 For all we know, the thread could see false or true at any given moment. 就我们所知,线程在任何给定时刻都可能看到false或true。 Caches could just be randomly flushed for no reason in particular, the thread could retain its cache, etc etc. 无需特别说明,就可以随意清除缓存,线程可以保留其缓存等。

It could be because your code is experiencing side effects from System.out.print , which internally writes to a synchronized method: 这可能是因为您的代码正在遭受System.out.print副作用,该副作用在内部写入同步方法:

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

( Source - DocJar ) 资料来源-DocJar

The memory effects of synchronized could be flushing the cache and therefore impact your code. 同步的内存影响可能会刷新缓存,因此会影响您的代码。

As @Stephen C said, it could also be the JIT, which might hoist the boolean check because it assumes that the value can't change due to another thread. 正如@Stephen C所说,它也可能是JIT,它可能会进行布尔检查,因为它假定值不会由于另一个线程而改变。

So out of the three different possibilities mentioned so far, they could all be factors to contribute to how your code behaves. 因此,在到目前为止提到的三种不同的可能性中,它们都可能是影响代码行为的因素。 Visibility is a factor, not a determiner. 可见性是一个因素,而不是决定因素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM