问个简单的 Java 重排序的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
guixiexiezou
V2EX    Java

问个简单的 Java 重排序的问题

  •  
  •   guixiexiezou 2020-05-08 22:04:54 +08:00 3520 次点击
    这是一个创建于 1984 天前的主题,其中的信息可能已经有所发展或是发生改变。

    也学了很久 java 了,但对重排序那块还是不太理解。直接上代码吧

    public class App { private static boolean flag = false; private static int cnt = 0; public static void main(String[] argv) throws Exception{ new Thread(() -> { while (!flag) { try { // Thread.sleep(2000L); } catch (Exception e) { e.printStackTrace(); } // System.out.println(cnt); } System.out.println("----a end"); }).start(); Thread.sleep(1000L); new Thread(() -> { refresh(); }).start(); } public static void refresh() { System.out.println("--stat"); flag = true; cnt = 3; System.out.println("---end"); } } 

    执行这个代码,必然是不可以看到----a end这条输出的,但我们把上面的注释代码,随便取消注释 1 个,就可以看到输出----a end,然后程序结束。

    有大佬可以简单讲下经验吗

    17 条回复    2020-05-09 15:00:17 +08:00
    falsemask
        1
    falsemask  
       2020-05-08 22:40:29 +08:00   1
    https://www.zhihu.com/question/263528143/answer/270308453 和这个类似,这个应该是可见性的问题。Java 多线程可见性问题还挺复杂,我看的时候遇到了好几个问题都没找到答案
    zzl22100048
        2
    zzl22100048  
       2020-05-08 23:45:40 +08:00 via iPhone
    这是可见性问题吧,空循环不让 cpu 没法从主存同步变量
    avk458
        3
    avk458  
       2020-05-09 00:28:19 +08:00
    第一个显视线程注释掉那两行代码后就等于是空 while 了,好像叫做 busy-spin waiting 。这个线程会一直等待下去,当然也就看不到`----a end`吧?
    ffkjjj
        4
    ffkjjj  
       2020-05-09 09:54:00 +08:00
    我觉得, 因为对于 flag 变量, 我们没有显示的对它设置线程的同步, 编译器认为 flag 不会被多个线程共享, 因此代码被 JIT 优化了, 所以出现程序不结束的情况.

    ```java
    if(!flag){
    while (true) {
    try {
    // Thread.sleep(2000L);
    } catch (Exception e) {
    e.printStackTrace();
    }
    // System.out.println(cnt);
    }
    }

    ```
    如果关掉 JIT 编译, 程序就可以正常结束.
    ffkjjj
        5
    ffkjjj  
       2020-05-09 10:01:44 +08:00
    即使是空循环, 系统也不是把所有 cpu 执行时间都分配给此线程.
    125113483
        6
    125113483  
       2020-05-09 11:10:38 +08:00
    变量可见性问题 如果把 flag 上 volatile 修饰就可以 结束循环 正常输出

    把代码 System.out.println 或 Thread.sleep 注释放开也可以结束循环 正常输出 是因为 println 方法被 synchronized 修饰 你可以点到方法里看一下 会刷新 flag 缓存 Thread.sleep 一样也会刷新 都是同步方法 如果你把这两个代码改成其他的 比如 cnt=2 这种非同步代码 一样会出现死循环
    yuxing1171
        7
    yuxing1171  
       2020-05-09 11:28:34 +08:00
    这与重排序有关系? 是可见性的问题吧,第一个线程一直 busy,没时间同步 flag 的值,而如果去过任意注释,CPU 就有时间同步 flag 的值。 用 volatile 修饰 flag,可以起到即时同步的目的。
    guixiexiezou
        8
    guixiexiezou  
    OP
       2020-05-09 11:58:38 +08:00
    @zzl22100048 原来如此,多谢了
    guixiexiezou
        9
    guixiexiezou  
    OP
       2020-05-09 11:58:54 +08:00
    @avk458 明白了,多谢
    guixiexiezou
        10
    guixiexiezou  
    OP
       2020-05-09 12:02:15 +08:00
    @ffkjjj 还真是,测试了下关掉 jit 确实是可以正常输出的,多谢啦
    guixiexiezou
        11
    guixiexiezou  
    OP
       2020-05-09 12:07:19 +08:00
    @125113483 多谢了,按照你的说法测试了下,确实会如此。但如果强制关闭 JIT,就会发发现可以结束了。所以我还是更倾向于是 JIT 的内部优化结果
    guixiexiezou
        12
    guixiexiezou  
    OP
       2020-05-09 12:10:04 +08:00
    @yuxing1171 确实是可见性的问题,昨晚说错了。另外你说的`第一个线程一直 busy,没时间同步 flag 的值`这个我是不认可的,安装其他楼层 说法,关掉 JIT 就可以正常结束。我倾向于这种说法,jvm 遇到这种空的死循环,压根就不会去同步数据(开启 JIT 的情况)
    ffkjjj
        13
    ffkjjj  
       2020-05-09 12:11:12 +08:00
    @guixiexiezou #10 但是这种说法, 我不知道怎么解释循环里面去掉注释之后可以同步的问题..
    ccpp132
        14
    ccpp132  
       2020-05-09 12:16:54 +08:00
    很简单啊,有别的函数调用的话,编译器就不能把 cnt 当作本地变量优化掉,相当于有 memory barrier 的作用。
    guixiexiezou
        15
    guixiexiezou  
    OP
       2020-05-09 12:23:57 +08:00
    @ccpp132 简单看了下,最主要的原因还是注释的代码都是同步方法,如果我们有别的函数调用的话(非同步方法),会发现结果是一样的,还是会死循环
    125113483
        16
    125113483  
       2020-05-09 14:50:29 +08:00
    @guixiexiezou 不是因为空的死循环不同步数据,你如果 volatile 修饰,空的死循环一样会同步啊 其实 sleep 也不是同步方法,他只是释放了 cpu 资源,cpu 资源一旦空闲 JVM 会完成优化 会同步工作内存和主存 完成内存的可见性
    ccpp132
        17
    ccpp132  
       2020-05-09 15:00:17 +08:00
    @guixiexiezou 是的,这些函数有 memory barrier 的效果,有些函数没有。和 cpu 资源没有关系的,内存可见性并不要 cpu 空闲。这种明显是没有 memory barrier 的提示被编译器或者 jit 直接优化掉了。如果是可见性的问题稍等就同步了,这个例子里是感觉不出来的。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4178 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 10:15 PVG 18:15 LAX 03:15 JFK 06:15
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86