int b = 0;
int c = 0;
synchronized(this) { -> monitorenter
Load内存屏障
Acquire内存屏障
int a = b;
c = 1; => synchronized代码块里面还是可能会发生指令重排
Release内存屏障
} -> monitorexit
Store内存屏障
java的并发技术底层很多都对应了内存屏障的使用,包括synchronized,他底层也是依托于各种不同的内存屏障来保证可见性和有序性的
按照可见性来划分的话,内存屏障可以分为Load屏障和Store屏障。
Load屏障的作用是执行refresh处理器缓存的操作,说白了就是对别的处理器更新过的变量,从其他处理器的高速缓存(或者主内存)加载数据到自己的高速缓存来,确保自己看到的是最新的数据。
Store屏障的作用是执行flush处理器缓存的操作,说白了就是把自己当前处理器更新的变量的值,都刷新到高速缓存(或者主内存)里去
在monitorexit指令之后,会有一个Store屏障,让线程把自己在同步代码块里修改的变量的值都执行flush处理器缓存的操作,刷到高速缓存(或者主内存)里去;然后在monitorenter指令之后会加一个Load屏障,执行refresh处理器缓存的操作,把别的处理器修改过的最新值加载到自己高速缓存里来
所以说通过Load屏障和Store屏障,就可以让synchronized保证可见性。
按照有序性保障来划分的话,还可以分为Acquire屏障和Release屏障。
在monitorenter指令之后,Load屏障之后,会加一个Acquire屏障,这个屏障的作用是禁止读操作和读写操作之间发生指令重排序。在monitorexit指令之前,会加一个Release屏障,这个屏障的作用是禁止写操作和读写操作之间发生重排序。
所以说,通过 Acquire屏障和Release屏障,就可以让synchronzied保证有序性,只有synchronized内部的指令可以重排序,但是绝对不会跟外部的指令发生重排序。
synchronized:
(1)原子性:加锁和释放锁,ObjectMonitor
(2)可见性:加了Load屏障和Store屏障,释放锁flush数据,加锁会refresh数据
(3)有序性:Acquire屏障和Release屏障,保证同步代码块内部的指令可以重排,但是同步代码块内部的指令和外面的指令是不能重排的