个人随笔
目录
并发(二)、从一段简单的代码论述volatile不能保证原子性
2021-04-15 22:47:18

我们知道volatile只能保证可见性和有序性,加了volatile的内存变量会遵循缓存一致性协议,z这很好的保证了可见性,具体为啥缓存一致性协议可以保证可见性,见笔记并发(一)、MESI(缓存一致性协议)

这篇文章用一个简单的例子解释下为啥volatile不能保证原子性,代码如下;

  1. public class VolatileTest {
  2. private volatile int count = 0;
  3. private void add() {
  4. count++;
  5. }
  6. private void print() {
  7. System.out.println(count);
  8. }
  9. public static void main(String[] args) {
  10. VolatileTest volatileTest = new VolatileTest();
  11. for(int i=0;i<10000;i++) {
  12. new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. volatileTest.add();
  16. }
  17. }).start();
  18. }
  19. try {
  20. Thread.sleep(2000);
  21. } catch (InterruptedException e) {
  22. // TODO Auto-generated catch block
  23. e.printStackTrace();
  24. }
  25. volatileTest.print();
  26. }
  27. }

这段代码执行结果有时候是10000,有时候却到不了10000,为什么呢?

我们知道count++其实是分为两步,第一步是取到count的值,然后再进行加1,所以在内存中该变量的逻辑步骤如下:

第一步

刚开始,count=0在内存中,如下

第二步

然后线程1加载副本到缓存中,由缓存一致性协议可以知道,此时状态为E独占。

第三步

然后线程2也加载到副本缓存中,由缓存一致性协议可以知道,此时两个线程count缓存行的状态为S共享。

第四步

然后线程1把缓存中的count传到CPU进行加一操作,线程2也把缓存中的count传到CPU中进行加1操作

第五步

两个CPU同时修改变量count,并同时向总线发出将各自的缓存行更改为M状态的情况,此时总线会采用相应的裁决机制进行裁决,将其中一个置为M状态,另一个置为I状态,且I状态的缓存行修改无效,这里假设线程1修改成功。


那么线程2的count将会变为I无效的状态,此时,相当于本次轮休白做了。

所以可以得出结论,volatile不能保证原子性。

 13

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号