JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在它仅仅描述的是一组约定或规范,通过这组规范定义了程序中(尤其是多线程)各个变量的读写访问方式并决定一个线程对共享变量的写入何时以及如何变成对另一个线程可见,关键技术点都是围绕多线程的原子性、可见性和有序性展开的。
重点说明:
(1)我们定义的所有共享变量都储存在物理主内存中
(2)每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
(3)线程对共享变量所有的操作都必须先在线程自己的工作内存中进行后写回主内存,不能直接从主内存中读写(不能越级)
(4)不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来进行(同级不能相互访问)
1-happens-before之8条
(1)次序规则:一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作;
(2)锁定规则:一个unLock操作先行发生于后面((这里的“后面”是指时间上的先后))对同一个锁的lock操作;
(3)volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的,这里的“后面”同样是指时间上的先后。
(4)传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
(5)线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作
(6)线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
(7)线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread::join()方法是否结束、Thread::isAlive()的返回值等手段检测线程是否已经终止执行。
(8)对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。意思:对象没有完成初始化之前,是不能调用finalized()方法的。
2-volatile的内存语义
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量
所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取。
3-内存屏障
内存屏障(也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作),避免代码重排序。内存屏障其实就是一种JVM指令,Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatile实现了Java内存模型中的可见性和有序性,但volatile无法保证原子性。
4-volatile变量规则
volatile重排序规则如下图:
在每一个volatile写操作前面插入了StoreStore屏障:
在每一个volatile写操作后面插入了StoreLoad屏障:
在每一个volatile读操作后面插入了LoadLoad屏障:
在每一个volatile读操作后面插入了LoadStore屏障:
5-使用场景
5.1-单一赋值可以,复合运算赋值不可
volatile int a = 10;
volatileboolean flag = false;
比如i++之类的操作不行;
5.2-状态判断
判断业务是否结束
public class TestVolatile {
static volatile boolean flag=true;
public static void main(String[] args) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t---come in");
while (flag){
}
System.out.println(Thread.currentThread().getName()+"\t---flag设置为false,程序终止");
},"t1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag=false;
System.out.println(Thread.currentThread().getName()+"\t---flag修改完成");
},"t2").start();
}
}
5.3-开销较低的读,写锁策略
public class TestCount {
private volatile int value;
//利用volatile保证单一读操作的可见性
public int getValue(){
return value;
}
//利用synchronized保证复合操作原子性
public synchronized int setValue(){
return value++;
}
}
5.4-DCL 单例模式检测对象
public class SafeSingleton {
private volatile static SafeSingleton singleton=null;
private SafeSingleton(){}
public static SafeSingleton getInstance(){
if(singleton==null){
synchronized (SafeSingleton.class){
if(singleton==null){
singleton=new SafeSingleton();//此处非原子操作,new 对象,没有初始化完成就可能赋值了,利用volatile禁止new 对象中的操作重排
}
}
}
return singleton;
}
}