2013-05-02 200 views
2

在审查this question我注意到这个代码:这段代码为什么会失败?

class MyThread extends Thread { 
    private boolean stop = false; 

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

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

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

+0

如何调用'setStop()'?来自MyThread的相同实例还是不同? – 2013-05-02 19:18:50

+0

我不知道,不幸的是这是一个抽象的例子,从我链接到 – 2013-05-02 19:25:25

+0

的问题setStop会从不同的线程调用。所有运行方法必须做的就是返回,它不需要标志。 – 2013-05-02 19:53:17

回答

5

实例变量stop需要是易失性的,否则不能保证其他线程会看到它的变化。在工作中有许多相互冲突的兴趣:线程想要一致的程序状态视图,CPU希望能够缓存数据,JVM希望能够重新排序指令。使实例变量volatile变量意味着它不能被缓存,而且会发生这种情况 - 在建立限制指令重新排序的关系之前。

请参阅this other answer(+1),其中列举了可能会发生的重新排序而不标记变量volatile的一个好例子。

(顺便using interruption for thread cancellation最好使用一个实例变量)

+0

你应该提到提升这是失败的第一个原因。 – 2013-05-02 19:23:33

+0

@JohnVint“悬挂”? – user2246674 2013-05-02 19:24:48

+1

@ user2246674我创建了自己的答案来解决吊装问题。 – 2013-05-02 19:28:32

0

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

1

变量stop必须声明为volatile。

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

6

JIT编译器可以重新排序读取并在应用程序中,只要

  1. 的动作顺序一致,
  2. 改变的行动不违反线程内语义写道。

这只是一种奇特的说法,所有的动作看起来应该像只有一个线程执行一样。所以,你可以得到JIT重新编译你的代码看起来像这样

class MyThread extends Thread { 
    private boolean stop = false; 

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

     } 
    } 
    } 

这就是所谓法律优化提升。它仍然像串行一样运行,但在使用多线程时提供令人惊讶的结果。

通过声明字段易失性,您告诉Java不要执行任何重新排序。与Nathan Hughes提到的内存一致性

+0

JVM将检查'doSomeWork()'是否在提升之前设置了“stop”字段?我认为是的,否则,优化似乎不是最佳:) – dlev 2013-05-02 19:33:55

+0

@dlev是的,绝对!否则会导致可怕的优化。 – 2013-05-02 19:35:53

+1

伟大的信息,谢谢! – 2013-05-02 19:51:21