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