volatile 为什么不能保证线程安全

参考 https://blog.csdn.net/chenaima1314/article/details/78723265

cpu与内存的工作架构。cpu的各个核心有自己的寄存器(存储核心计算的临时结果)与缓存(缓冲内存与cpu核心处理速度的差异),各个核心共享同一块主内存。

volatile 为什么不能保证线程安全

JVM的线程模型,这里的工作内存就是指寄存器与缓存,主内存就是主存。也就是了解JVM的线程模型与 CPU-内存架构的映射。

volatile 为什么不能保证线程安全

 

除了线程模型,还缺内存模型,及其与 CPU-内存架构 的映射。 这样一来,线程模型-内存模型的关联也有了解释。栈是线程私有的,线程之间共享堆。

volatile 为什么不能保证线程安全

 

volatile 关键字作用

首先volatile仅保证了给工作内存的副本赋值时,同时写入到内存中,这个过程是原子的。

也就是从工作内存赋值指令assign,到传输值到内存store并写入write,这三个操作是原子操作。

 

缓存一致性协议(MESI协议)

这个协议首先将缓存分为了4种状态(MESI),核心对缓存的数据操作按读写操作以及读写对象是否是核心私有的cache也分为了4种(LR,LW,RR,RW),其次规定了在各个状态下,每个操作会发生的事情以及缓存状态的变化。

原文理出了大话处理器的全状态机表格,我们这里的例子中仅关心的是当cache状态是I时,进行LW的操作。

  • 状态变化:I——>M
  • 事情:从内存中取数据,在Cache中修改,状态变成M;如果其它Cache有这份数据,且状态为M,则要先将数据更新到内存;如果其它Cache有这份数据,则其它Cache的Cache line状态变成I

 

 

volatile 下线程不安全的例子

volatile 为什么不能保证线程安全

按照 volatile 的原子性, 3,4是原子的, 5,6,7是原子的。问题出在5的时候由于MESI协议,I状态下进行了LW,发生的不寻常行为只是从内存中先读一份新的值再修改。

因为 volatile 没办法保证指令的执行顺序,核心1先取内存值存了缓存,没想到缓存状态被核心2执行的操作弄成 invalid了,而核心1 恢复执行权时对于 invalid 状态的缓存不会重新LR,而是仍然继续自加操作得到了错误的结果,即使后面 volatile 原子性化的赋值写回内存指令对于前面计算发生的错误也无力回天。