java while不会退出

洗袜子的少年 发布于 10/12 16:47
阅读 319
收藏 0

static boolean done = false;
public static void main(String[] args) {
    new Thread(() -> {
        while (!done);
        System.out.println("exited----");
    }).start();

    new Thread(() -> {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        done = true;
        System.out.println("exiting----");
    }).start();
}

求教:为什么第一个线程的while不会退出?

加载中
1
tcxu
tcxu

预备知识:
JMM(Java 内存模型)定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。
线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是 JMM 的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

楼主的案例场景如图,说明如下。

  1. static boolean done = false; 表明,done 是主方法启动的两个线程 (这里依次成为 A 与 B) 的共享属性/内存。它存于主内存。
  2. done 初始值为 false。第一个线程 A 运行,将主内存里的 done,复制到它的工作内存(本地内存A)。由于本地内存A里的 done 始终是 false,导致 执行 while (!done);  成了死循环, 所以不会跳出while 循环,退出。
  3. done 初始值为 false。第二个线程 B 运行,将主内存里的 done,复制到它的工作内存(本地内存B)。随后,线程 B 启动,将本地内存B里的 done 变为 true。但这种变更,改变不了主内存和本地内存A中的 done。就是说,不可能终止第一个线程(A)的while循环。

楼上,坚强的小二、hbyckyle、温安适 等 建议 添加关键词 volatile 的 办法,确实可行。

参考:
JMM和底层实现原理

Java中volatile关键字的最全总结

 

 

洗袜子的少年
洗袜子的少年
应该是JMM的原因,以后得注意点了:joy:
Lime1
Lime1
回复 @Lime1 : 补充一下。发现并非“不是空循环体”导致退出,在循环体赋值操作时是不会导致退出的,而是因为System.out.println的原因,后查阅资料System.out似乎是线程安全的对象 接@tcxu的答案 猜测:可能是因为同步块导致,线程循环时重新读取了主内存变量,且同时也因为另一个线程已提交变量到主内存中 因为楼层回复不能插入图片,所以测试代码发到其他楼了,欢迎指正错误~!
Lime1
Lime1
但 不是空循环体的话,“static boolean done = false; ”这种形式还是会退出的
0
洗袜子的少年
洗袜子的少年
哪位大佬可以帮忙解释下
0
天天向上zhougf
天天向上zhougf

为什么不会? 会啊

洗袜子的少年
洗袜子的少年
应该是while后边没有加分号:joy:
洗袜子的少年
洗袜子的少年
你可以试下,我这试的是不会退出的
s
solo丶慕颜
。。。 为什么你会有那么多的exited? while 后面的分号没加吧。。。
0
坚强的小二

加个 volatile 试试

static volatile boolean done = false;

0
h
hbyckyle

这肯定不会退出啊 读到变量表里面是false 你用

volatile static boolean done = false

就可以了

h
hbyckyle
回复 @洗袜子的少年 : 我个人认为你说的两种都是会让出cpu的情况,不管是sout还是sleep都是会让出cpu的,因此这种情况下后续会发生线程切换,切换到当前线程的时候,done的值可能会重新读取,但是没有让出cpu操作的话,是会一直执行的,虽然是空循环,这个时候done的值就是变量表里面的值,也是线程刚创建时候读进来的false(个人理解,不一定对,我也是新手- -)
洗袜子的少年
洗袜子的少年
while(!done){ System.out.print("");}这样是可以的:sweat_smile:
洗袜子的少年
洗袜子的少年
但是在循环体里写一个打印语句,或sleep下就可以的
0
温安适
温安适
volatile static boolean done = false

volatile 保证可见性,否则第二个线程的修改第一个线程看不见

温安适
温安适
回复 @洗袜子的少年 : System.out.print("")或者sleep(1),这就有时间差了,在这个时间差后,第一个线程就能看到第二个线程修改的值了。
洗袜子的少年
洗袜子的少年
while(!done){ System.out.print("");}这样或者循环体里slee(1)就可以退出
0
谷歌火狐和IE

如果循环体执行很快,比如循环体为空,Java内存模型会把变量的值缓存起来,导致别的线程更新后,当前线程无法看到变量最新的值,所以循环会一直不退出。

System.out.print("")或者sleep(1)都是比较耗时的操作,print调用的IO相关的API

以上均是瞎猜😜

0
Lime1
Lime1

以下是测试代码

返回顶部
顶部