请教下 Java 的 volatile 以及一点多线程的疑问 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Hatter
V2EX    Java

请教下 Java 的 volatile 以及一点多线程的疑问

  •  
  •   Hatter 291 天前 2404 次点击
    这是一个创建于 291 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在看 java 的 volatile 这个东西,写了一块代码想测试一下,代码如下: image.png

    我的理解是,如果变量 a 没有 volatile 修饰,那么理论上就应该线程 1 打印一次,其他线程空转不再打印

    但目前发现有一些情况:

    1 、如果不加 Thread.sleep 这行代码,程序能直接打印到 100 ,加了 sleep 才是死循环

    2 、去掉 sleep ,但把打印语句换成 System.out.println(Thread.currentThread().getName()+":"+su.getA()); 结果也会变成死循环

    所以想请教下 v 友两个问题

    1 、为什么我没加 volatile 也能打印到 100

    2 、为什么换了个输出语句就又打印不到 100 了。。。

    13 条回复    2025-01-10 10:03:01 +08:00
    wuyiccc
        1
    wuyiccc  
       291 天前
    没看懂。。。
    Hatter
        2
    Hatter  
    OP
       291 天前
    @wuyiccc 就是理论上不用 volatile 修饰 a 的话,线程内对 a 做了修改,变量 a 的变动对线程 2 和 3 应该是不可见的,理论上线程 2 和 3 就是应该一直死循环才对;但我发现有时候居然能打出来,然后做了一点看起来不相关的修改之后又打印不出来了
    wolfie
        3
    wolfie  
       291 天前
    volatile 是 内存可见性问题,不用 volatile 修饰,不代表永远无变化感知,只是延迟。
    psjay
        4
    psjay  
       291 天前
    不可见的意思是:不立即可见。
    你这个 Demo 用来测试 volatile 没有意义,即便你加了 volatile ,对 a 的访问也无法保证原子性。
    F281M6Dh8DXpD1g2
        5
    F281M6Dh8DXpD1g2  
       291 天前
    "就是理论上不用 volatile 修饰 a 的话,线程内对 a 做了修改,变量 a 的变动对线程 2 和 3 应该是不可见的"
    理论不是这样的
    https://jpbempel.github.io/2015/05/26/volatile-and-memory-barriers.html
    Hatter
        6
    Hatter  
    OP
       290 天前
    @psjay 是我对不可见的理解有问题了。。多谢
    原子性这块倒是明白,并不是想测试原子性相关的情况
    Hatter
        7
    Hatter  
    OP
       290 天前
    @liprais 多谢老哥,还是搞错了“是不是”的问题
    cloudzhou
        8
    cloudzhou  
       290 天前
    保证可见性,只是说一定能看到,happens before
    但不是说,一直不可见,理论上,随着内存的同步周期变化,是能感知到变化的
    dongdong12345
        9
    dongdong12345  
       290 天前   3
    你的问题应该和编译器有关。java 好像在 1.8 开始就默认使用分层编译了。
    它编译的大概步骤是:解释阶段 -> C1 编译器 -> C2 编译器
    如果你只使用 C1 编译器的话代码执行是没有问题的,你可以增加 VM 参数尝试:-XX:TieredStopAtLevel=3 。因为 C1 编译器的 level 是 1 ,2 ,3 ,所以设置-XX:TieredStopAtLevel 不超过 3 ,就不会使用 C2 编译器。
    如果你只想使用 C2 编译器,你可以设置-XX:TieredStopAtLevel=4 。不过默认就会启动,可以不必尝试了。
    当然,对于 volatile 的处理,C1 和 C2 都是遵循 java 内存模型的。
    wangliran1121
        10
    wangliran1121  
       290 天前   1
    Hatter
        11
    Hatter  
    OP
       290 天前
    @wangliran1121 感谢回复,不懂的东西越来越多了 T T
    zengyufei
        12
    zengyufei  
       286 天前
    针对 “1 、为什么我没加 volatile 也能打印到 100”,我来说一个正确答案:因为 System.out.println() 的调用。
    参考: https://blog.csdn.net/weixin_47474875/article/details/128916512
    zengyufei
        13
    zengyufei  
       286 天前
    针对 Thread.sleep 有无导致结果不同,我也从 GPT 那里得到了答案。

    忙循环导致子线程 a 无暇从主线程 a 那里得到更新。

    大白话就是:我一直在忙,没有空去新的东西。

    给你对比实验感知一下这个事情:

    1 、不加 volatile
    2 、Thread.sleep(100); 这句留着
    即与你图片的代码完全一致即可。

    运行只能 thread1 打印了一下。

    对比实验动作 1:thread2 的 while 循环代码块加入 Thread.sleep(1);
    为什么先从 thread2 开始加,因为你 thread1 执行完毕 a+1 了,下一个肯定是 thread2 先执行。
    你可以先加 thread3 ,运行结果肯定没变化。
    先从 thread2 开始加,会得到结果 thread2 也打印了语句。

    对比实验动作 2:thread3 的 while 循环代码块加入 Thread.sleep(1);
    运行结果大概打印 3-20 条,具体看概率。
    因为 System.out.println 会模仿 volatile 功能,导致 thread1 偶尔会更新 a ,最后 thread1 会陷入忙循环。

    对比实验动作 3:最后 thread1 的 while 循环代码块加入 Thread.sleep(1);运行,你会正确打印 1-100 ,这是程序最初的目的。

    最后,你自己总结一下。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2649 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 14:54 PVG 22:54 LAX 07:54 JFK 10:54
    Do have faith in what you're doing.
    ubao msn 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