Java中的volatile为什么不能保证原子性

在之前的文章中java中的volatile关键字的作用_ErwinNakajima的博客-CSDN博客_java中的volatile关键字的作用

已经介绍过volatile只能保证可见性和有序性(禁止指令重排序优化),既然保证了变量的可见性,有人会有这样的疑问:

volatile变量对线程立即可见,那对volatile变量的修改都能立刻反应到其他线程。

换句话说,volatile变量在各个线程中是一致的,所以volatile变量的运算在多线程下是线程安全的,也就是可以保证原子性。

但是这里面忽略了一个问题,默认运算本身是原子操作,但是实际上对volatile i++操作并不是原子操作,从主内存读->加操作->写到主内存,下面我们分析一下这个问题。

测试案例

先通过一段Java案例代码对其进行相关的说明:

package jmm;

public class VolatileDemo {

    public static volatile int num = 0;

    public static void increase(){
        num ++;
    }

    public static void main(String[] args) throws InterruptedException {
        //1、创建10个线程
        Thread[] threads = new Thread[10];

        for (int i = 0; i < 10; i++) {
            //十个线程都调用普通方法
            threads[i] = new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    //num ++ 操作执行1000次
                    increase();
                }
            });
            threads[i].start();
        }

        //等待所有线程执行完成  才继续执行下面的代码
        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println(num);
    }
}

该代码执行后得到的结果多种多样,如:

在这里插入图片描述

在这里插入图片描述

为什么会出现这样的结果呢,接下来采取数据处理流程图进行解释说明。 

原因

在Java中,针对多线程共同处理数据操作,通常以如下方式进行:(假设2个cpu)

在这里插入图片描述

大家都知道num ++ 不具备原子性

在CPU对其进行数据处理时,分为两部操作。
读:num = 0;
写:num = num + 1;

 在博客缓存一致性协议(MESI)中,说到数据处理操作有如下几个步骤:
1、CPU1获取主存中,num变量信息时,将其从中拷贝副本高速缓存中,并将其MESI状态标记为E(独享)

在这里插入图片描述

2、如果此时CPU2也读取了数据,由于CPU1对其他CPU具有总线嗅探机制,当监听到被监听的数据经过BUS总线,则会将数据的状态信息变更为S(共享)状态。

在这里插入图片描述

3、由不同CPU去对自身独有缓存进行加锁操作,BUS总线中的总线裁决判断哪个CPU加锁有效。

上面是数据操作的大致流程,但想过一个问题没有:

当CPU对缓存行加锁成功时,使其他CPU对该数据状态进行失效处理。

 但是,如果此时数据,已经在寄存器中经过处理,只是还并未 assign(赋值) 到指定工作内存中呢?如下所示:

在这里插入图片描述

此时CPU1工作内存(高速缓存)中将数据进行失效处理

但如果num = num + 1这个命令还在寄存器中处理,并未assign(赋值)工作内存中,就会出现下面的情况:

 在这里插入图片描述

CPU1中,针对num ++却在寄存器中正在被操作。
CPU2获取到缓存行加锁操作,将CPU1中的缓存数据进行失效处理,但并不能寄存器中的数据也进行失效操作。

导致CPU1中,寄存器将数据num = num + 1进行运算操作,再执行assign(赋值)操作,写回工作内存中。
此时CPU1 工作内存对应数据并不存在,寄存器会将该信息进行写入工作内存并将其写回主存

此时会导致CPU1CPU2两个线程操作,都执行了一次num = 1write(写入)操作,也就是两个线程都进行了操作,但结果却是1!!!

最后总结

volatile能够保证数据变化,其他线程及时可见性

但不能保证数据原子性操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值