这段代码为什么会失败?
在审查this question我注意到这个代码:这段代码为什么会失败?
class MyThread extends Thread {
private boolean stop = false;
public void run() {
while(!stop) {
doSomeWork();
}
}
public void setStop() {
this.stop = true;
}
}
但是我不明白为什么会这样失败。其他线程是否无法访问“实际”停止变量?
实例变量stop
需要是易失性的,否则不能保证其他线程会看到它的变化。在工作中有许多相互冲突的兴趣:线程想要一致的程序状态视图,CPU希望能够缓存数据,JVM希望能够重新排序指令。使实例变量volatile变量意味着它不能被缓存,而且会发生这种情况 - 在建立限制指令重新排序的关系之前。
请参阅this other answer(+1),其中列举了可能会发生的重新排序而不标记变量volatile的一个好例子。
(顺便using interruption for thread cancellation最好使用一个实例变量)
你应该提到提升这是失败的第一个原因。 – 2013-05-02 19:23:33
@JohnVint“悬挂”? – user2246674 2013-05-02 19:24:48
@ user2246674我创建了自己的答案来解决吊装问题。 – 2013-05-02 19:28:32
其他线程不能保证看到停止更新值 - 你需要建立关系“之前发生”。最简单的方法是停止波动。
变量stop必须声明为volatile。
虽然我更喜欢使用中断来停止线程。
JIT编译器可以重新排序读取并在应用程序中,只要
- 的动作顺序一致,
- 改变的行动不违反线程内语义写道。
这只是一种奇特的说法,所有的动作看起来应该像只有一个线程执行一样。所以,你可以得到JIT重新编译你的代码看起来像这样
class MyThread extends Thread {
private boolean stop = false;
public void run() {
if(!stop){
while(true){
}
}
}
这就是所谓法律优化提升。它仍然像串行一样运行,但在使用多线程时提供令人惊讶的结果。
通过声明字段易失性,您告诉Java不要执行任何重新排序。与Nathan Hughes提到的内存一致性
JVM将检查'doSomeWork()'是否在提升之前设置了“stop”字段?我认为是的,否则,优化似乎不是最佳:) – dlev 2013-05-02 19:33:55
@dlev是的,绝对!否则会导致可怕的优化。 – 2013-05-02 19:35:53
伟大的信息,谢谢! – 2013-05-02 19:51:21
如何调用'setStop()'?来自MyThread的相同实例还是不同? – 2013-05-02 19:18:50
我不知道,不幸的是这是一个抽象的例子,从我链接到 – 2013-05-02 19:25:25
的问题setStop会从不同的线程调用。所有运行方法必须做的就是返回,它不需要标志。 – 2013-05-02 19:53:17