2

在查看这个问题时,我注意到了这段代码:

class MyThread extends Thread {
  private boolean stop = false;

  public void run() {
    while(!stop) {
      doSomeWork();
    }
  }

  public void setStop() {
    this.stop = true;
  }
}

但是我不明白为什么会失败。其他线程是否无法访问“实际”停止变量?

4

4 回答 4

6

JIT 编译器可以重新排序应用程序中的读取和写入,只要

  1. 动作是顺序一致的,并且
  2. 改变的动作不违反线程内语义。

这只是一种奇特的说法,所有动作都应该以与仅由单个线程执行的方式相同的方式发生。因此,您可以让 JIT 重新编译您的代码,使其看起来像这样

class MyThread extends Thread {
  private boolean stop = false;

  public void run() {
    if(!stop){
       while(true){

       }
    }
  }

这是一种称为提升的合法优化。它的行为仍然与串行相同,但在使用多个线程时会提供令人惊讶的结果。

通过声明一个 volatile 字段,您是在告诉 Java 不要执行任何重新排序。除了 Nathan Hughes 提到的内存一致性

于 2013-05-02T19:28:16.503 回答
5

实例变量stop必须是易失的,否则不能保证其他线程会看到它的变化。工作中存在很多相互冲突的利益:线程想要程序状态的一致视图,CPU 想要能够缓存数据,JVM 想要能够重新排序指令。使实例变量易失意味着它不能被缓存,并且在建立限制指令重新排序的关系之前发生。

有关在不将变量标记为 volatile 的情况下可能发生的重新排序的一个很好的示例,请参见这个其他答案(+1)。

(顺便说一下,使用中断来取消线程比使用实例变量更可取。)

于 2013-05-02T19:18:01.560 回答
1

变量 stop 必须声明为 volatile。

虽然我更喜欢使用中断来停止线程。

于 2013-05-02T19:19:36.910 回答
0

不能保证其他线程看到更新的停止值 - 您需要建立“发生在之前”的关系。最简单的方法是让 stop volatile。

于 2013-05-02T19:19:00.530 回答