可见性、原子性和有序性讲解

前传

我们的CPU、内存、I/O设备都在不断迭代,不断朝着更快的方向的努力。但是有一个核心矛盾一直存在,就是三者速度的差异。CPU和内存的速度差异可以形容为:CPU是天上一天,内存是地上一年;内存和I/0设备的速度差异就更大了,内存是天上一天,I/O设备是地上十年。

可见性

缓存导致的可见性问题

在单核时代,所有线程都是在同一CPU上执行的,所有线程都是操作同一个CPU的缓存,一个线程对缓存的写,对另一个线程来说一定是可见的。例如下图中,线程A和线程B都是操作同一个CPU里面的缓存,所以线程A更新了变量V的值,那么线程B之后再访问变量V,得到的一定是V的最新值。

一个线程对共享变量的修改,另外一个线程能够立刻看到,称为可见性。

多核时代,每颗CPU都有自己的缓存,这时CPU缓存与内存的数据一致性就没那么容易解决了,当多个线程在不同的CPU上执行时,这些线程操作的是不同的CPU缓存。 下图中,线程A操作的是CPU-1上的缓存,而线程B操作的是CPU-2上的缓存,很明显,这个时候线程A对变量V的操作对应线程B而言就不具有可见性了。

直觉告诉我们应该是2000000,但实际情况可能是1000000到2000000之间的任意一个值。

假设线程A和线程B同时执行,那么第一次都会讲count=0读到各自的CPU缓存里,执行完count+=1之后,各自CPU缓存里的值都是1,同时写入内存后,我们会发现内存中是1,而不是我们期待的2。之后由于各自的CPU缓存里都有count的值(1),两个线程都是基于CPU缓存里的count值来计算的,所以导致最终中count的值要小于2000000,。这就是缓存可见性的问题。

如果循环的次数更大,我们会发现效果更明显,最终的count值差值越大,原因是两个线程不时同时启动的,有一个时差。

原子性

count+=1,至少需要三条CPU指令。

指令1:首先,需要把count变量从内存加载到CPU寄存器。 指令2:之后,在寄存器中执行+1操作。 指令3:最后,将结果写入内存(缓存机制导致的可能是写入CPU缓存而不是内存) 如果线程A在指令1执行之后做线程切换,线程A和线程B按照下图的序列执行,那么我们发现两个线程都执行了count+=1操作,得到的结果不是我们期望的2,而是1.

我们潜意识里面觉得 count+=1 这个操作是一个不可分割,就像原子一样,线程的切换可以发生在count+=1之前,也可以发生在count+=1之后,但是不会发生在中间。

我们把一个或者多个操作在CPU执行的过程中不被中断的特性称为原子性。

有序性

有序性实例

转载于:https://juejin.im/post/5c8b5ad6f265da2d881b7011

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值