首页 > 解决方案 > 为什么这段代码会陷入死循环

问题描述

我正在使用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));
        }
    }
}

标签: javaconcurrencyinfinite-loop

解决方案


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

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

解决方案:

  1. 改变setStartisStart成为synchronized方法。
  2. isStart将字段声明为volatile
  3. 不要编写自己的State类,而是使用标准java.util.concurrent类来进行同步;例如CountDownLatchjavadoc)。

我建议您花时间学习 Oracle Java 并发教程课程:


1 - Java 语言规范的“Java 内存模型”部分中列出了为什么会发生这种情况的技术解释。但是,JLS 不是以初学者可以理解的方式编写的,这部分特别困难。
2 - 事实上,JLS 并没有说明是否会看到更改。实际行为可能取决于程序员无法控制的一系列因素。


推荐阅读