我使用的是 jdk1.8。
这段代码直接运行就死循环了,但是如果我加上注释的代码,就可以正常运行了。我试了很多代码,只要涉及到加锁的操作,就可以正常运行。
public class StateTest {
public static void main(String[] args) {
State state = new State();
new Thread(new Work(state)).start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
state.setStart(true);
System.out.println("the main thread is finished");
}
static class State {
private boolean isStart = false;
public boolean isStart() {
return this.isStart;
}
public void setStart(boolean start) {
this.isStart = start;
}
}
static class Work implements Runnable {
private State state;
public Work(State state) {
this.state = state;
}
@Override
public void run() {
int i = 0;
//endless loop
while (!this.state.isStart()) {
i++;
// if open this code,it will be ok
// synchronized (this) {
//
// }
}
System.out.println(String.format("work start run after %s loops", i));
}
}
}
答案 0 :(得分:2)
问题在于 State
实例不是线程安全的。如果一个线程调用 setStart
,第二个线程调用 isStart
,则第二个线程可能看不到第一个线程设置的值1。
您在 setStart
实例上使用 State
,以便一个实例可以向第二个实例发出信号以结束循环。如果第二个线程没有看到状态变化(由于上述原因),则循环不会终止2。
解决方案:
setStart
和 isStart
更改为 synchronized
方法。isStart
字段声明为 volatile
。State
类,而是使用标准的 java.util.concurrent
类进行同步;例如CountDownLatch
(javadoc)。我建议您花时间学习有关并发的 Oracle Java 教程课程:
1 - Java 语言规范的“Java 内存模型”部分中列出了为什么会发生这种情况的技术解释。但是,JLS 的编写方式不是初学者可以理解的,这部分特别困难。
2 - 事实上,JLS 并没有说明是否会看到或不看到更改。实际行为可能取决于程序员无法控制的一系列因素。
答案 1 :(得分:0)
不保证 isStart
值更新在新创建的线程中可见。
由于多个线程访问 isStart
字段,您希望将其标记为 volatile
以确保更新的变量值将始终写入主内存而不是 CPU 缓存。
static class State {
private volatile boolean isEnd = false;
// getters & setters
}