synchronize
存储在Markword对象头 前4个字节当中的第一个字节的后3个比特位
偏向锁101>轻量级锁 [000]>重量级锁 [010]
volatile
解决的多线程下的线程可见性问题,用到了内存屏障 缓存锁
在MESI协议中,每个Cache line有4种状态,分别是:
1、M(Modified)
这行数据有效,但是被修改了,和内存中的数据不一致,数据只存在于本Cache中
2、E(Exclusive)
这行数据有效,和内存中的数据一致,数据只存在于本Cache中
3、S(Shared)
这行数据有效,和内存中的数据一致,数据分布在很多Cache中
4、I(Invalid)
这行数据无效
每个Core的Cache控制器不仅知道自己的读写操作,也监听其它Cache的读写操作,假如有4个Core:
1、Core1从内存中加载了变量X,值为10,这时Core1中缓存变量X的cache line的状态是E;
2、Core2也从内存中加载了变量X,这时Core1和Core2缓存变量X的cache line状态转化成S;
3、Core3也从内存中加载了变量X,然后把X设置成了20,这时Core3中缓存变量X的cache line状态转化成M,其它Core对应的cache line变成I(无效)
当然了,不同的处理器内部细节也是不一样的,比如Intel的core i7处理器使用从MESI中演化出的MESIF协议,F(Forward)从Share中演化而来,一个cache line如果是F状态,可以把数据直接传给其它内核,这里就不纠结了。
CPU在cache line状态的转化期间是阻塞的,经过长时间的优化,在寄存器和L1缓存之间添加了LoadBuffer、StoreBuffer来降低阻塞时间,LoadBuffer、StoreBuffer,合称排序缓冲(Memoryordering Buffers (MOB)),Load缓冲64长度,store缓冲36长度,Buffer与L1进行数据传输时,CPU无须等待。
1、CPU执行load读数据时,把读请求放到LoadBuffer,这样就不用等待其它CPU响应,先进行下面操作,稍后再处理这个读请求的结果。
2、CPU执行store写数据时,把数据写到StoreBuffer中,待到某个适合的时间点,把StoreBuffer的数据刷到主存中。
因为StoreBuffer的存在,CPU在写数据时,真实数据并不会立即表现到内存中,所以对于其它CPU是不可见的;同样的道理,LoadBuffer中的请求也无法拿到其它CPU设置的最新数据;
由于StoreBuffer和LoadBuffer是异步执行的,所以在外面看来,先写后读,还是先读后写,没有严格的固定顺序。
引发指令重排
对于处理器来说,内存屏障会导致cpu缓存的刷新,刷新时,会遵循缓存一致性协议。
lock:解锁时,jvm会强制刷新cpu缓存,导致当前线程更改,对其他线程可见。
volatile:标记volatile的字段,在写操作时,会强制刷新cpu缓存,标记volatile的字段,每次读取都是直接读内存。
final:即时编译器在final写操作后,会插入内存屏障,来禁止重排序,保证可见性
缓存一致性协议:https://blog.csdn.net/m15517986455/article/details/83273183
指令重排序、内存屏障 https://blog.csdn.net/qq_41973594/article/details/110452849