为什么这段代码会陷入无限循环

时间:2021-05-20 09:37:10

标签: java concurrency infinite-loop

我使用的是 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));
        }
    }
}

2 个答案:

答案 0 :(得分:2)

问题在于 State 实例不是线程安全的。如果一个线程调用 setStart,第二个线程调用 isStart,则第二个线程可能看不到第一个线程设置的值1

您在 setStart 实例上使用 State,以便一个实例可以向第二个实例发出信号以结束循环。如果第二个线程没有看到状态变化(由于上述原因),则循环不会终止2

解决方案:

  1. setStartisStart 更改为 synchronized 方法。
  2. isStart 字段声明为 volatile
  3. 不要编写自己的 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
}