Java线程的内存不可见性

356 阅读2分钟

内存不可见性

所谓内存不可见性,就是线程对某个共享变量在线程自己的缓冲中存在副本的时候对主内存中共享变量的值是不可见的,看不见主存中的值。

图片.png

如图是一个2CPU的架构系统,每个CPU都有自己的控制器和运算器,每个CPU也有自己的一级缓存,在图中的架构中,这两个CPU核心共享一个二级缓存。

假设某个时刻,这两个CPU核心分别运行着两个线程A和线程B,都要去同时访问主存中的一个共享变量x

线程操作一个共享变量时,它首先从主存中拉取并复制一份变量放置到自己的工作内存中,然后在工作内存中对变量进行修改,处理完之后将工作内存中的值重新写回到主存中。

初始状态,两级缓存都为空, x=0,假设A先执行,首先会在A的L1 Cache中查询变量x, 发现没有命中,再去查询L2 Cache,也没有命中,进而到主存中查询x,查询到x的变量后,将x的值复制到一级和二级缓存中.然后A对x加一,使得x=1,之后,刷新主存数据x=1.到目前一切正常,然后B开始运行,同样查询L1 Cache没有命中,查询L2 Cache, 命中,x = 1,没有任何问题,B现在也对x加一,使得x=2,然后更新缓存,B的L1 Cache与共享的L2 Cache与主存中的x均为2.到目前,也是一切正常,但如果现在A在L1 Cache缓存失效之前再次使用x时,首先查询A的L1 Cache,立即命中x, 此时x=1,再次进行加一操作,得到x = 2,重新更新x的信息,之后,主存,L2 Cache中的x均为2,但是按照逻辑, x被操作了3次,正确的值应该为3,但是实际结果是2,这就丢失了操作了,这就是内存不可见带来的问题。

在java中,使用volatile关键字修饰共享变量可以解决这个问题,怎么解决的呢?

既然我们知道造成上述问题的原因是线程对共享变量在主存的值不可见,那么我们就让线程在访问共享变量时,不从本地的工作内存中查,直接从主存中拉取最新的值即可。