V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Newyorkcity
V2EX  ›  问与答

Java 的 volatile:能否在只使用 volatile 不使用更高级多线程安全措施的前提下,让这段代码按预期工作?

  •  
  •   Newyorkcity · 2021-04-24 22:58:47 +08:00 · 1449 次点击
    这是一个创建于 1338 天前的主题,其中的信息可能已经有所发展或是发生改变。
    public class VolatileTest {
    
        private volatile static int a = 0;
        private static int cnt = 0;
    
        public static void test() {
            if (a == 0) {
                a = 1;
                cnt++;
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread[] threads = new Thread[2];
            for (int i = 0; i < 10000; i++) {
                for (int j = 0; j < 2; j++) {
                    threads[j] = new Thread(VolatileTest::test);
                }
                for (Thread thread : threads) {
                    thread.start();
                }
                for (Thread thread : threads) {
                    thread.join();
                }
                if (cnt == 2) {
                    System.out.println("false!");
                }
                a = 0;
                cnt = 0;
            }
            System.out.println("finish");
        }
    }
    

    在反复启动程序的尝试中,一般出现的情况是控制台出现一次 false!,偶尔会出现两次或者不出现。

    希望能达成 false!永远不会被输出,即 cnt 结束时总为 1 的结果,能否在不引入 Atomic*类,synchrnozied,加锁等更高级工具,只使用 volatile 实现?

    cnt ++ 代表着希望被执行一次的代码。可能会有多个线程调用 VolatileTest.test(),但 cnt++只执行一次。

    谢谢

    17 条回复    2021-04-25 18:26:31 +08:00
    geelaw
        1
    geelaw  
       2021-04-24 23:38:36 +08:00 via iPhone
    不能。
    Newyorkcity
        2
    Newyorkcity  
    OP
       2021-04-24 23:43:07 +08:00
    @geelaw 那 volatile 的保持可见性,以及一些文章提到 volatile boolean 可能靠谱的情况是什么呢。。
    Jooooooooo
        3
    Jooooooooo  
       2021-04-24 23:49:14 +08:00   ❤️ 1
    当然不行, 两个线程会同时运行到 if(a==0) 并且发现 a 确实都是 0
    Newyorkcity
        4
    Newyorkcity  
    OP
       2021-04-24 23:50:04 +08:00
    @geelaw 是因为 voliatle 永远无法保证 『线程 b 执行完 if a == 0 』这件事不发生在 『线程 a 执行完 if a == 0 』和『线程 a 执行 a = 1 』之间吗?
    Leviathann
        5
    Leviathann  
       2021-04-25 01:12:38 +08:00 via iPhone
    借楼问一下
    我看项目里以前的代码有用 volatile 修饰一个 service 里的 map,而 map 除了初始化有个赋值为 new hashmap,其他都只有 get set
    这种 volatile 有什么用? volatile 应该只涉及到 map 的这个引用不涉及到内部元素的吧
    还是我理解错了
    cubecube
        6
    cubecube  
       2021-04-25 01:57:34 +08:00
    @Leviathann 你理解得没错,除非会并发访问并存在更新 map 本身为另外一个 hashmap or null,正常 map 没必要加
    EscYezi
        7
    EscYezi  
       2021-04-25 09:08:32 +08:00 via iPhone
    @Newyorkcity #4 是的,因为判断 a 是否为 0 和给 a 赋值 1 是两个操作,想要达到预期的效果必须把这个两个操作变成一个原子操作
    bxb100
        8
    bxb100  
       2021-04-25 09:57:26 +08:00
    操作和可见不是一件事情吧
    eric96
        9
    eric96  
       2021-04-25 09:57:32 +08:00
    两个线程同时判断到 a==0,然后进入条件语句,将 a 赋值为 1.volatile 只是保证了可见性和不重排,但是你对 a 的判断和赋值是两个操作,不是原子的
    securityCoding
        10
    securityCoding  
       2021-04-25 10:02:42 +08:00 via Android
    @Leviathann 这是在瞎用了,volatile 一般结合 cas 实现无锁并发读写。
    eric96
        11
    eric96  
       2021-04-25 10:07:03 +08:00
    要么加锁,要么用 Atomic 。其实可以看下 Atomic 的实现,也就是 volatile 加上 cas 操作
    dqzcwxb
        12
    dqzcwxb  
       2021-04-25 10:38:30 +08:00
    @eric96 #11 也可以用 LongAdder
    theOneMe
        13
    theOneMe  
       2021-04-25 11:41:35 +08:00
    多于两步的操作,要么无锁同步加可见行,要么加锁。
    inhzus
        14
    inhzus  
       2021-04-25 13:04:01 +08:00 via iPhone
    有 自己用 volatile 实现一遍 cas,也挺简单的 /doge
    wqhui
        15
    wqhui  
       2021-04-25 13:52:50 +08:00
    没记错的话 volatile 只是每次去内存读值,不使用缓存,保证每次读到的值是最新的。但是运气不好的话,一个线程读到了 a=0,在做 a=1 的赋值操作前这段时间,其他线程也是能够进来的
    wolfie
        16
    wolfie  
       2021-04-25 13:53:42 +08:00
    要是能实现,Atomic* 情何以堪。
    LukeChien
        17
    LukeChien  
       2021-04-25 18:26:31 +08:00
    如果你希望原子执行的代码都如 cnt++ 这么简单,那你可以换个单核的 CPU :)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   873 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:50 · PVG 04:50 · LAX 12:50 · JFK 15:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.