刚学多线程,请问下面这段程序为什么停不下来啊?

2021-04-03 21:14:26 +08:00
 Frankhong

书上说的不是很清楚,只说是编译器优化的结果。具体是什么原因呢?编译器再怎么优化 ready=true 不还得执行吗,怎么 while(!ready)就退出不了了呢?

public class NoVisibility {
    private static boolean ready;  
    private static int number;  
  
    private static class ReaderThread extends Thread {  
        public void run() {  
            while (!ready);
            System.out.println(number);  
        }  
    } 
  
    public static void main(String[] args) throws InterruptedException {  
        new ReaderThread().start();  
        Thread.sleep(1000);
        number = 42;  
        ready = true;  
        Thread.sleep(10000);
    }  
}
6452 次点击
所在节点    Java
41 条回复
oska874
2021-04-03 21:26:07 +08:00
while ( ready )直接优化成 while ( 1 )了,执行的时候和 ready 已经没关系了。
lsry
2021-04-03 21:31:27 +08:00
因为你 ready 在新建的线程和 main 线程中各有一个备份,在 main 线程中修改 ready 无法修改你新建线程的 ready 备份,如果让你新建的线程看到 main 修改的值,在前面加 volatile 即可。
az467
2021-04-03 21:39:14 +08:00
月经问题。。。

不是编译器优化,是 jvm 优化。

你没把 jvm 的 jit 关掉,所以 jvm 把

while (!ready);

优化成了

if (ready == false)
while (true);
Kasumi20
2021-04-03 21:41:49 +08:00
确实不是不是编译器优化,反编译回来是原来的代码。

那究竟是 2 楼还是 3 楼说的对呢
PythonYXY
2021-04-03 21:41:55 +08:00
建议了解下 Java 内存模型( JMM )
ipwx
2021-04-03 21:49:29 +08:00
没学过 Java,但是最近我写 C++,碰到这种多线程读写指示变量,我都是上 std::atomic 的。。。
choice4
2021-04-03 21:49:56 +08:00
二楼说的对,Ready 要保证内存可见性
Jooooooooo
2021-04-03 21:53:52 +08:00
搜一下 jmm 有真相.

具体说和多核 cpu 看不见其它 cpu 的值有关系

ready=true cpu1 执行完, 执行 while(!ready) 的 cpu2 看不见
yitingbai
2021-04-03 22:05:33 +08:00
2 楼的解决方案没问题, 另外我试了一下,while()中写一行代码也可以停, java 搞了这么久, 今天学到了
ignor
2021-04-03 22:12:48 +08:00
@az467 纯好奇,这种明显错误的优化有什么存在的必要呢?
RicardoY
2021-04-03 22:13:48 +08:00
可见性 看一下 Java 内存模型
RicardoY
2021-04-03 22:15:06 +08:00
你 sleep 一下其实也可以 但是不知道为什么 sleep 可以 原子变量 /加锁 /volatile 是可以的
az467
2021-04-03 22:23:14 +08:00
@ignor

了解一下 JMM,你就会知道这种优化非但没有错误,反而完全是正确的。

因为变量没有被 volatile 修饰,所以 jvm 不用保证变量在线程间的可见性。
既然不用保证,那当然可以完全不保证,甚至可以反过来“保证”变量完全不可见。

这就像很多人开了 gcc 的 O3 优化,然后惊奇地发现程序运行结果跟自己预期不一致。
不好意思,是你把代码写错了。
brucewuio
2021-04-03 22:37:00 +08:00
volatile 咯 内存可见性
ignor
2021-04-03 23:03:31 +08:00
@az467 感觉还是没太理解……
我所了解到的可见性,是指线程对变量的修改,能够“及时地”被其他线程所看到
所以 volatile 解决的应该是“及时地”的问题,而不是“能不能”的问题,也就是说,即使没有 volatile,这种修改也应该“能够”被观察到,只是时间上有延迟而已。因为它是个变量,应该是可以被修改的,所以这种“能够”不应该被优化掉才对吧?
az467
2021-04-03 23:17:39 +08:00
@ignor 你可以这么理解,“时间上的延迟”在这里是无穷大。
ZeawinL
2021-04-03 23:31:59 +08:00
这里是两个线程,线程 1 赋值后对线程 2 未必可见。
ignor
2021-04-03 23:33:56 +08:00
@az467 这就是难以理解的地方,为什么可以认为把一个有限的时间间隔改为无穷大是一种正确的优化呢?
wwqgtxx
2021-04-04 00:13:45 +08:00
@ignor 因为作为优化器,并不能(准确说是并不会去)预测是否会有其他线程修改该变量,所以只要当前线程没不会有对该变量的修改操作,就能直接认定为该变量为常量
francis59
2021-04-04 00:33:12 +08:00
空循环被 JIT 优化了,禁用 JIT 就行,加 JVM 参数-Xint,在解释模式运行

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/767839

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX