Android并发编程之图文解析volatile关键字

相信很多人对于volatile关键字既熟悉又陌生,熟悉是对这个名字很熟悉,陌生是对他的原理和用法很陌生,最近几天通过查阅大量资料和书,终于对volatile有了一定的理解,写此博客一来做了记录,二来使大家减少学习成本

volatile为什么不能保证原子性?

现在我们的手机都是多核的,也就是说同时有好几颗CPU在工作,每颗CPU都有自己的Cache高速缓存,因为CPU的速度特别快,而内存的读取操作相对于CPU的运算速度来说很慢,所以就会拖累CPU的效率,引入Cache就是为了解决这个问题的,CPU先把需要的数据从内存中读到Cache中,然后直接和Cache来打交道,Cache的速度很快,因此可以保证CPU的工作效率,当Cache中的数据改变后,再将被改变的数据写回内存中。
这里写图片描述
首先我们分析一下多线程在访问一个普通的(没有加volatile修饰符)的变量的过程
1.CPU1和CPU2先将count变量读到Cache中,比如内存中count=1,那么现在两个CPU中Cache中的count都等于1
2.CPU1和CPU2分别开启一个线程来对count进行自增操作,每个线程都有一块自己的独立内存空间来存放count的副本。
3.线程1对副本count进行自增操作后,count=2 ; 线程2对副本count进行自增操作后,count=2
4.线程1和线程2将操作后的count写回到Cache缓存的count中
5.CPU1和CPU2将Cache中的count写回内存中的count。
那么问题就来了,线程1和线程2操作的count都是一个count副本,这两个副本的值是一样的,所以进行了两次自增后,写回内存的count=2。而正确的结果应该为count=3。这就是多线程并发所带来的问题

如果变量count用volatile修饰了可以解决这个问题吗?

如果一个变量加了volatile关键字,就会告诉编译器和JVM的内存模型:这个变量是对所有线程共享的、可见的,每次JVM都会读取最新写入的值并使其最新值在所有CPU可见。我们再来看一下线程在访问一个加了volatile修饰符的变量的过程
这里写图片描述
当count用volatile关键字修饰后,CPU1对count的值更新后,在写回内存的同时会通知CPU2 count值已经更新了,你需要从内存中获取count最新的值!

注意:这里说CPU1通知CPU2其实是不严谨的,其实这是缓存一致性机制在其作用,缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行数据时,会使缓存行无效,当CPU1将新数据写回内存后,会修改该数据在内存中的内存地址,CPU2通过嗅探在总线上传播的数据来检查自己的缓存行对应的内存地址是否被修改,如果被修改则将CPU2的该数据缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到CPU2的缓存行里。其实并不是CPU1通知CPU2,而是CPU2自己去嗅探。

其实大家只要明白了原理,怎么说也无所谓,就像好多地方都说volatile修饰的变量,线程直接和内存交互,不会保存副本。而实际上线程还是会保存副本,只不过CPU每次都会从内存中拿到最新的值,并且改变数据之后立马写回内存,看上去就像线程直接和内存交互一样。

然后CPU2中的线程如果需要使用到count的时候,就会再从内存中读取count的值来更新自己的Cache。这看上去似乎解决了我们的问题,其实问题依然存在,我们来分析一下:
比如当前count=1,CPU1和CPU2的Cache中的count都等于1,CPU1中的线程1对count进行了自增操作,然后CPU1更新了内存中count的值,并且通知CPU2 count的值已经改变,然后CPU2从内存中将count=2读到了Cache中,并且线程2开始执行count的自增操作,而就在CPU2刚刚将count的值读回Cache的时候,CPU1又更新了count的值,此时count=3,并且通知CPU2,但是此时线程2已经开始执行了,线程2已经将count=2拷贝到自己的内存空间中了,所以即使CPU2再次更新自己Cache中的count=3,也来不及了,线程2操

  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值