CPU 层可见性
多核cpu 在执行多线程任务时,每个线程都会持有一份数据缓存;
操作共享区域时,是操作的数据缓存中的内容,然后同步至主内存;
so, 多线程操作存在A,B线程同时改写共享区域时,数据不一致的情况;
volatile 关键词,基于内存屏障进行解决问题,写线程操作会( 发送 invalidate 消息给其他CPU )使得读线程的共享数据缓存失效;从而达到可见性的效果;
扩展: 在汇编层面,volatile关键字在操作使,会多一个lock 指令,lock 指令可以基于总线锁 或者缓存锁机制达到可见性的效果;
总线锁和缓存锁
总线锁:对多cpu下,一个处理器对共享内存操作时,发出lock信号,禁止其他处理器通过总线访问共线内存中的数据。开销较大
缓存锁:基于CPU缓存一致性协议,常见协议MSI , MESI ,MOSI;
- M(Modify) : 表示共享数据只缓存在当前 CPU 缓存中,并且是被修改状态,也就是缓存的数据和主内存中的数据不一致
- E(Exclusive) : 表示缓存的独占状态,数据只缓存在当前CPU 缓存中,并且没有被修改
- S(Shared) : 表示数据可能被多个 CPU 缓存,并且各个缓存中的数据和主内存数据一致
- I(Invalid) : 表示缓存已经失效
内存屏障
解决的问题:cpu 层面,无法得知软件层面代码执行顺序,将代码排序操作交由软件执行;
就是将 store bufferes 中的指令写入到内存,从而使得其他访问同一共享内存的线程的可见性
- Store Memory Barrier(写屏障) :告诉处理器在写屏障之前的所有已经存储在存储缓存(store -bufferes)中的数据同步到主内存,简单来说就是使得写屏障之前的指令的结果对屏障之后的读或者写是可见的
- Load Memory Barrier(读屏障) :处理器在读屏障之后的读操作,都在读屏障之后执行。配合写屏障,使得写屏障之前的内存更新对于读屏障之后的读操作是可见的
- Full Memory Barrier(全屏障): 确保屏障前的内存读写操作的结果提交到内存之后,再执行屏障后的读写操作
JMM
Java Memory Model , 即,提供合理的禁用缓存和禁止重排序的方法;解决内存访问的可见性和有序性;