循环内未更新变量“runner”

ltskdhd1  于 2021-07-12  发布在  Java
关注(0)|答案(1)|浏览(343)

像这样,我有两条线。sleeprunner线程向列表中添加一些随机数,然后将flag更改为true并休眠。主线程等待sleeprunner线程,直到sleeprunner对象中的标志从false变为true,然后主线程将中断sleeprunner线程,程序将结束。
但问题是,当while循环在主线程中没有主体代码时,变量runner不会在循环内更新,换句话说,sleeprunner线程将标志从false更改为true后,程序不会结束。所以我试着在idea中使用调试工具,但是程序结束得很顺利。如果我在主线程的while循环体中编写一些代码,比如system.out.println()或thread.sleep(1),程序也会成功结束。太不可思议了!有人知道为什么会这样吗?谢谢。

public class Test1 {
        public static void main(String[] args) {
            SleepRunner runner = new SleepRunner();
            Thread thread = new Thread(runner);
            thread.start();
            while(!(runner.isFlag())){
                /*try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
            }
            System.out.println("END");
            thread.interrupt();
        }
    }

public class SleepRunner implements Runnable {
        private boolean flag = false;

        public boolean isFlag() {
            return flag;
        }

        @Override
        public void run() {
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep((long) (Math.random() * 200));
                }
                catch (InterruptedException e) {
                    System.out.println("Interrupted");
                }
                int num = (int) (Math.random() * 100);
                System.out.println(Thread.currentThread().getName() + " " + num);
                list.add(num);
            }

            flag = true;

            System.out.println("30 Seconds");
            try {
                Thread.sleep(30000);
            }
            catch (InterruptedException e) {
                System.out.println("Interrupted in 30 seconds");
            }
            System.out.println("sleep runner thread end");
        }
    }
uqzxnwby

uqzxnwby1#

你违反了java内存模型。
以下是jmm的工作原理*:
每当读取或更新任何字段(来自任何对象)时,每个线程都会抛出一枚硬币。在头部,它将复制并更新/读取。在尾巴上,它不会。您的工作是确保您的代码正确运行,而不管coinflip是如何到达的,并且您不能在单元测试中强制coinflip。硬币不一定是“公平的”。硬币的行为取决于音乐播放器中播放的音乐、幼儿的突发奇想和月亮的相位(换句话说,任何更新/读取都可以对本地缓存副本执行,也可以不执行,直到java实现)。
你可以有把握地得出这样的结论:唯一正确的方法是确保线不会翻转硬币。
实现这一目标的方法是建立所谓的“先到先得”关系。建立它们主要是通过使用同步原语,或者通过调用使用同步原语的方法来完成的。例如,如果我这样做:
螺纹x:

synchronized(x) {
    x.foo();
    System.out.println(shared.y);
    shared.y = 10;
}

螺纹y:

synchronized(x) {
    x.foo();
    System.out.println(shared.y);
    shared.y = 20;
}

然后你建立了一个关系:代码块a在代码块b之前,反之亦然,但你至少建立了它们必须按顺序运行。
因此,这将打印 0 10 或者 0 20 ,保证。没有同步块,它可以合法打印 0 0 也。所有3个结果都是可以接受的结果(javalang规范说没问题,任何你认为没有意义的bug都会被忽略为“按预期工作”)。 volatile 也可以使用,但挥发性相当有限。
一般来说,由于这无法得到充分的测试,所以在java中只有3种方法可以正确地执行线程:
“in the large”:使用Web服务器或其他处理多线程的应用程序框架。你不写 psv main() 方法,该框架会这样做,而您编写的只是“处理程序”。你的处理程序根本就不涉及任何共享数据。处理程序要么不共享数据,要么通过一个设计好的总线来共享数据,比如在可序列化事务隔离模式下的db,或者rabbitmq或其他一些消息总线。
“in the small”:使用fork/join并行化大型任务。当然,任务的处理程序不能使用任何共享数据。
阅读《实践中的并发》(本书),更喜欢使用java.util.concurrent包中的类,并且通常是这方面工作的Maven,因为以任何其他方式执行线程都可能导致编程错误,而您的测试可能不会发现这些错误,但会在生产时爆炸,或者将不会导致实际的多线程处理(例如,如果您过于热心地同步所有内容,那么除了一个核心之外,您的所有核心都会等待,并且您的代码实际上会比单线程运行慢得多)。

  • )完整的解释是关于一本书的价值。我只是给你一个过于简单化的亮点,因为这只是一个简单的答案。

相关问题