指令重排序
从java源码到最终执行指令序列,会经历以下三种重排序:编译器优化重排序,指令集并行重排序,内存系统重排序。
java允许编译器和未处理器进行优化,这可能会影响到未正确同步的代码。如图:
如果某次执行表现出了这样的行为,那么我们可能得出这样的结论,指令 4 要在指令 1 之前执行,指令 1 要在指令 2 之前执行,指令 2 要在指令 3 之前执行,指令 3要在指令 4 之前执行。
然而,从单个线程的角度看,只要重排序不会影响到该线程的执行结果,编译器就可以对该线程中的指令进行重排序。如果指令 1 与指令 2 重排序,那就很容易看出为什么会出现 r2 == 2 和 r1 == 1 这样的结果了。 ***即CPU为了提高指令执行效率,会在一条指令执行过程中(比如去内存读数据),去同时执行另一条指令,前提是两条指令没有依赖关系***。
内存屏障
可以把内存屏障理解成一堵墙,墙两边的指令无法被Cpu乱序执行。CPU提供了三个汇编指令串行化运行读写保证读写有序性:
SFENCE:在该指令前的写操作必须在该指令后的写操作前完成
LFENCE:在该指令前的读操作必须在该指令后的读操作前完成
MFENCE:在该指令前的读写操作必须在该指令后的读写操作前完成
jvm内存屏障规范
LoadLoad屏障:举例语句是Load1; LoadLoad; Load2(这句里面的LoadLoad里面的第一个Load对应Load1加载代码,然后LoadLoad里面的第二个Load对应Load2加载代码),此时的意思就是,在Load2及后续读取操作从内存读取数据到CPU前,保证Load1从主内存里要读取的数据读取完毕。
StoreStore屏障:举例语句是 Store1; StoreStore; Store2(这句里面的StoreStore里面的第一个Store对应Store1存储代码,然后StoreStore里面的第二个Store对应Store2存储代码)。此时的意思就是在Store2及后续写入操作执行前,保证Store1的写入操作已经把数据写入到主内存里面,确认Store1的写入操作对其它处理器可见。
LoadStore屏障:举例语句是 Load1; LoadStore; Store2(这句里面的LoadStore里面的Load对应Load1加载代码,然后LoadStore里面的Store对应Store2存储代码),此时的意思就是在Store2及后续代码写入操作执行前,保证Load1从主内存里要读取的数据读取完毕。 ***StoreLoad屏障:***举例语句是Store1; StoreLoad; Load2(这句里面的StoreLoad里面的Store对应Store1存储代码,然后StoreLoad里面的Load对应Load2加载代码),在Load2及后续读取操作从内存读取数据到CPU前,保证Store1的写入操作已经把数据写入到主内存里,确认Store1的写入操作对其它处理器可见。
volatile 实现细节
1、字节码
ACC_VOLATILE
2、JVM
volatile写操作 前面加SS屏障 ,后面加SL屏障
StoreStoreBarrier
volatile写操作
StoreLoadBarrier
volatile读操作 前面加LL屏障,后面加LS屏障
LoadLoadBarrier
volatile读操作
LoadStoreBarrier
3.OS windows 上用的是lock指令实现(没用fence)
Sychronized 实现细节
1.字节码
ACC_SYCHRONIZED
monitorenter monitorexit
2.JVM
通过一些c和c++指令实现
3.OS
x86: lock cmpxchg