九、volatile与Java内存模型
一、被volatile修饰变量的两大特点
可见性。
有序性。
二、语义
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量。
所以volatile的写内存语义是直接刷新到主内存中,读内存语义是直接从主内存中读取。
三、内存屏障
1、概念
内存屏障(也称内存栅栏、内存栅障、屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访向的操作中的一个同步点,使得此点之前的所有读写操作都执行后,才可以开始执行此点之后的操作),避免代码重排序。内存屏障其实就是一种JVM指令,Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatle实现了Java内存模型中的可见性和有序性,但volatile无法保证原子性。
内存屏障之前的所有写操作都要回写到主内存。
内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果(实现了可见性)。
一句话:对一个volatile域的写, happens-before于任意后续对这个volatile域的读,也叫与后读。
2、四大内存屏障指令
public static void loadload();
public static void storestore();
public static void loadstore();
public static void storeload();
3、 volatile写
在每个volatile写操作的前面,插入一个StoreStore屏障。
在每个volatile写操作的后面,插入一个StoreLoad屏障。
4、 volatile读
在每个volatile读操作的后面,插入一个LoadLoad屏障。
在每个volatile读操作的后面,插入一个LoadStore 屏障。
四、volatile变量的读写过程
1.read:作用于主内存,将变量的值从主内存传输到工作内存。
2.load:作用于工作内存,将read从主内存传输的变量值放入工作内存变量副木中,即数据加载。
3.use:作用于工作内存,将工作内存变量副本的值传递给执行引擎,每当JVM遇到需要该变量的字节码指令时会执行该操作。
4.assign:作用于工作内存,将从执行引擎按接收的值赋值给工作内存变量.每当JVM遇到一个给变量赋值字节码指令时会执行该操作。
5.store:作用于工作内存,将赋值完毕的工作变量的值写回给主内存。
6.write:作用于主内存,将store传输过来的变量值赋值给主内存中的变量。
由于上述只能保证单条指令的原子性,针对多条指令的组合性原子保证,没有大面积加锁。所以,JVM提供了另外两个原子指令。
7.lock:作用于主内存,将一个变量标记为一个线程独占的状态,只是写时候加锁,就只是锁了变量的过程。
8.unlock:作用于主内存,把一个处于锁定状态的变量释放,然后才能被其他线程占用。
五、常见的volatile用法
1、单一赋值可以,but含复合运算赋值不可以(i++之类)。
2、状态标志,判断业务是否结束。
3、开销较低的读,写锁策略。
4、DCL双端锁的发布。