学习笔记:volatile为什么不能保证原子性

不保证原子性的例子

public class Test {
    public static volatile int data = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                Test.data++;
                System.out.println(data);
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                Test.data++;
                System.out.println(data);
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("最终的data为:" + Test.data);
    }
}

1
3
4
5
2
8
9
10
11
7
12
13
14
15
16
17
18
19
20
最终的data为:20

多线程同时工作去修改一个被volatile关键字修饰的共享变量的时候,比如两个线程对同一个变量volatile int i;进行i++操作,可能会导致运行结果跟预期不一致。这是因为volatile只能保证可见性和有序性,而不能保证原子性。

为什么不能保证原子性

Java内存模型
首先看一下上面这张图,分析是这样子的:
1、每个线程都有自己的一块工作内存,将主存里的数据缓存到自己的工作内存中。
此时,Threa1跟Thread2都通过read+load将 data = 0 缓存到自己的工作内存中去了。
2、cpu切换到Thread1时,Thread1就会将data = 0 use 进cpu里面计算data++的操作,但是没来得及将计算结果data = 1, assign回Thread1的工作内存中。cpu此时切换到了Thread2,然后在Thread2线程也做了同样的操作(将data = 0 use进cpu并执行data++),继续assign回Thread2的工作内存,并store+write将data = 1写回主存。
因为data是用volatile修饰的,根据缓存一致性协议,其他的线程(这里指的是Thread1)就会立即知道自己工作内存中的数据(data = 0)已经失效了。然而,并没有用!因为Thread1之前已经将data = 0 use 进cpu进行计算去了,cpu切换回Thread1时,Thread1将之前计算好的data = 1 assign 到自己的工作内存中,并store+write写回主存。通过以上的执行顺序,两个线程分别对data做了一次data++操作之后,得到的data = 1 而不是我们预期中的data = 2。
3、总结:多线程同时修改一个被volatile修饰的共享变量时,不能保证原子性,只能保证可见性和顺序性。

如何保证原子性

当然是加锁啦!不要问,问就是用synchronized…只有加了锁,让别的线程连读的机会都没有,才能够安安心心的搞完 read-load-use-assign-store-write全套而不被中途打扰。

public class Test {
    public static volatile int data = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronized (Test.class) {
                    Test.data++;
                    System.out.println(data);
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                synchronized (Test.class) {
                    Test.data++;
                    System.out.println(data);
                }
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("最终的data为:" + Test.data);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
最终的data为:20

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值