我们知道,线程之间的可见性能用volatile关键字来解决,那么它为什么能解决呢?
可见性问题的产生
CPU的高速缓存
缓存包含L1(L1d-数据缓存(存放数据)、L1i指令缓存(执行数据的指令码))、L2、L3,其中L3是CPU共享,而L1和L2是每个cpu独占的缓存空间。 这三级缓存都是集成在CPU内的缓存结构,他们的作用就是为CPU和主内存之间提供一个高速缓冲区,L1最靠近CPU核心;L2其次;L3再次。运行速度方面:L1最快、L2次快、L3最慢;容量大小方面:L1最小、L2较大、L3最大。CPU会先在最快的L1中寻找需要的数据,找不到再去找次快的L2,还找不到再去找L3,L3都没有那就只能去内存找了。因为L1、L2缓存是独立的,所以就会导致什么问题?
可见性问题!!
当多个线程并行执行一个共享数据时,都同时在本cpu的高速缓存中的时候,最新的数据对其他的cpu是不可见的!!所以CPU又引入了总线锁与缓存锁
总线锁和缓存锁
总线锁,首先得知道总线,其实总线就是负责将各个CPU连接到内存. 我们平时买电脑的时候,有个总线频率,比如1066MHz、1333MHz、1600MHz,频率越高,数据传输量越大. 那么总线锁可以理解为重量级锁或者悲观锁,当我一个cpu在处理数据时,其他cpu都是阻塞. 这样肯定不可取,所以又继续优化,就出来了缓存锁,相对于总线锁,降低了锁的力度,那么它怎么去保存缓存的数据一致性?
缓存锁引入了缓存一致性协议来保证,有MSI,MESI
.MOSI等等!
CPU为何要有高速缓存
代码语言:javascript复制 CPU中内置了少量的高速缓存以解决IO速度和CPU运算速度之间的不匹配问题。
带有高速缓存的CPU执行计算的流程:
- 程序以及数据被加载到主内存
- 指令和数据被加载到CPU的高速缓存
- CPU执行指令,把结果写到高速缓存
- 高速缓存中的数据写回主内存
CPU缓存一致性协议MESI
MESI 是指4中状态的首字母。每个Cache line有4个状态,它们分别是:
协议动态演示地址
超级好用 链接: MESI动态地址
具体的细节动态图里有详细体现,比我在这哔哔哔半天有用的多
总结Volatile作用 1.禁止编译器对代码进行某些优化 2.lock汇编指令,锁住缓存行,启动内存屏障,禁止指令重排,保证有序性 与可见性 当然,除了volatile来保证可见性外,我们从JDK1.5开始就引用了happens- before的概念来保证可见性!! 链接: Java并发编程——happens-before规则