volatile为什么不能保证原子性
小陈:老王,快来快来,上一篇结尾说volatile不能保证原子性,我现在迫不及待了...
老王:哈哈,来了,马上开搞......
老王:按照惯例,我还是先来给你画张图:
还是以 i++ 的那个例子为例,volatile int i = 0,假如两个线程A、线程B同时对 i 进行 ++ 操作如下:
上图存在一种情况就是,线程A、线程B如果几乎同时读取 i = 0 到自己的工作内存中。
线程A执行 i++ 结果后将 i = 1 赋值给工作内存;但是这个时候还没来的将最新的结果刷新回主内存的时候,线程B就读取主内存的旧值 i = 0 ,然后执行use指令将 i = 0的值传递给线程B去进行操作了。
即使这个时候线程A立即将 i = 1刷入主内存,那也晚了;线程B已经使用旧值 i = 0进行操作了,像这种情况计算结果就不对了。
老王:小陈,我上面的那个图讲解,你可以听懂嘛?
小陈:嗯嗯,看图解释就是,线程A的 i ++ 结果, 也就是 i = 1还没刷回主内存;线程B就执行 use指令将 i = 0传递给cpu了 ,导致线程B使用的就旧的值 i = 0去进行操作,得到结果是错的。
怎样才能保证原子性?
小陈:那如果要保证原子性,应该是怎么样子的?
老王:如果要保证原子性的话,落到底层实际还是需要进行加锁的,需要保证任意时刻只能有一个线程能执行成功。
比如在硬件层次或者对总线进行加锁,使得某一时刻只能有一个线程能执行i++ 操作,这样才能是不被中断的,才是原子性的。
现在现在这种情况,相当于就是两个线程同时进行了 i++操作,线程A的 i++ 操作还没结束;线程B的 i++ 操作就也同时进行着,这种情况不是原子的。
小陈:哦,是不是可以这么理解:
如果要保证原子性的话,同一时刻只能有一个线程或者CPU能够执行成功,底层是需要对硬件进行加锁的,只有某个CPU或者线程锁定了,享有独占的权限,那么它的操作才能是不被其它CPU或者线程打断的。
老王:没错,就是这个道理;你只有在硬件级别加锁了之后,享有独占的权限;你的操作才能是不被其它CPU或线程打断的。
小陈:好的,老王,这么说我就理解了。
老王:这一篇对volatile不能保障原子性的解释,你再多看看几遍,多理解一下。再对之前的文章再复习复习,包括内存屏障、java内存模型、MESI一致性协议等知识,从下一章看是我们就要进入新的学习了。
小陈:好啊老王,下面的篇章我们要学习什么知识啊?
老王:下面我们就要进入synchronized的学习了....