JMM 是抽象的内存规范
对变量有改操作后,会把结果回写回来。
,先用assign 把执行引擎的数据同步到工作内存中,然后从工作内存store出来,再用write到主内存中。
必须把它的工作内存的数据同步到主内存中
八大
java并发的三大特性:
- 可见性
- 原子性
- 有序性
可见性
每个线程只能看到自己的工作内存的更改, 加一个volatile可以及时的看到,但是不加volatile线程在一定的时间之后也会看到,只是看到的时机确定不了。
volatile是java并发轻量级的锁机制
线程B改了initFlag后就会主动通知线程A,让线程A去主内存再拿一次
加了volatile后字节码层面会给这个initFlag变量加一个ACC_VOLATILE标记
原子性
加了volatile也不能保证原子性,要用synchronized或者lock去保证原子性。
counter++ 相当于分了三步(读、自加、写回),这三步每一步是原子的,但是这三步加在一起不能保证原子性。因为如果执行了一步就被切到另一个线程后,另一个线程对counter修改后因为加了volatile会通知线程A 去主内存读新的,那之前执行的就会扔掉 就少加了一次。
有序性
指令重排
我们的java代码的顺序 不是CPU执行指令的顺序,他会进行指令重排比如a和b都依赖z变量 ,如果b指令是在很后面 就会被提前到和a指令一起加到缓存去
volatile可以解决指令重排
代码证明确实有指令重排的情况
-
第一种:x=0,y=1
-
第二种:x=1,y=0
- 第三种 x=1,y=1
- 指令重排则会出现 x=0,y=0
指令重排的危害
内存屏障
volatile 中加了内存屏障
我们也可以自己手动加内存屏障,用Unsafe类的以下三个方法: