java在单线程的条件下,是遵循as-if-serial语义
sychronzied实现可见性的过程
解锁前,把共享变量的最新值刷新到主内存中;
加锁时,清空工作内存,从而使用共享变量时需要从主内存中获取最新的值。
线程解锁前对共享变量的修改,在下次加锁时对其他线程可见。
synchronized和volatile比较
volatile实现可见性
深入来说:通过加入内存屏障和禁止重排序优化来实现的。
对volatile变量执行写操作时,会在写操作后加入一条store屏障指令。
对volatile变量执行读操作时,会在读操作前加入一条load屏障指令。
通俗地讲:
volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存,这样任何时刻,不同的线程总能看到该变量的最新值。
线程写volatile变量的过程:
线程读volatile变量的过程:
可见性分析
导致共享变量在线程间不可见的原因:
线程的交叉执行
重排序结合线程交叉执行
共享变量未及时更新
synchronized实现可见性代码:在读、写操作的方法上加上synchronized(对应上面出现问题的解决方案)
原子性
原子性
可见性
synchronized实现可见性的代码
java语言层面支持的可见性实现方式:
synchronized
volatile
synchronized实现可见性
JMM关于synchronized的两条规定:
线程执行互斥代码的过程:
获得互斥锁
清空工作内存
从主内存拷贝变量的最新副本到工作内存
执行代码
将更改后的共享变量的值刷新到主内存中
释放互斥锁
重排序
as-if-serial
重排序不会给单线程带来内存可见性问题
多线程中程序交错执行时,重排序可能会造成内存可见性问题。
可见性
java内存模型(JMM 【java Memory Model】)描述了java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。
两条规定
共享变量可见性实现的原理
volatile读写操作过程
线程执行互斥代码的过程
64位 long、double变量的读写可能不是原子操作
volatile只能保证变量的可见性,不能保证操作的原子性,所以只适用于一些特定场合
线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写,也就是说线程的与自己的工作内存交互,工作内存和主内存交互。线程与主内存不直接交互
对64位变量的读写可能不是原子操作
CPU在执行时会很快地刷新缓存
final也可以保证内存可见性
synchronized & volatile
volatile
ReentrantLock
保证自增操作的原子性
volatile不能保证变量复合操作的原子性
volatile实现可见性
volatile实现可见性
通过加入内存屏障和禁止重排序优化来实现的
当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将会被阻塞
可见性分析
导致共享变量在线程间不可见的原因
可见性分析
重排序不会给单线程带来内存可见性问题