JMM 浅析
什么是JMM?
JMM 即 Java 内存模型 Java Memory Model 简称 JMM。
本身是一种抽象的概念,并不真实存在,它仅仅描述的是一组约定或规范,通过这组规范定义了程序中(尤其是在多线程中)各个变量的读写访问方式并决定一个线程对共享变量的写入何时以及如何对另一个线程可见,关键技术点都是围绕多线程的原子性、可见性、有序性展开的。
JMM 能干嘛?
- 通过JMM来实现内存和内存之间的抽象关系
- 屏蔽各个硬件平台和操作系统的内存访问差异 以实现让 Java 程序在各种平台下都能达到一致的内存访问效果
可见性
原子性
是指一个操作是不能被打断的,在多线程环境下面操作不能被其他线程干扰
有序性
处理器在进行重排序的时候必须要考虑指令之间的数据依赖性
多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程间中使用的变量能否保证一致性是无法确定的,结果无法预测。
JMM规范下 多线程对变量的读写过程
JMM 规范下 happens-before 原则
在 JVM 中,如果一个操作执行的结果需要对另一个操作保持可见性,或者代码重排序,那么这两个操作之间必须存在 happens-before 原则,即存在逻辑上的先后关系
happers-before 8条规则
1. 次序规则
一个线程内按照代码顺序,写字前面的操作先行发生于写在后面的操作
2. 锁定规则
一个 unlock 操作先行发生于(时间上的先后)后面对同一个锁的 lock 操作
3. volatile 变量规则
对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的(同样指时间上的先后)
4. 传递规则
如果 A 操作先行发生于 B, B 操作先行发生于操作 C ,那么一定有 A 操作先行发生于 C 操作
5. 线程启动规则
Thread 对象的 start() 方法先行发生于这个线程对象的所有动作
6. 线程中断规则
对线程 interrupt() 方法的调用优先发生于被中断线程的代码检测到中断事件的发生
可以通过 Thread.interrupt() 方法来检测到是否发生过中断
也就是说可以先调用 interrupt() 方法来先设置过中断标志位,才能检测到中断发送
7. 线程终止规则
线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过 isAlive() 等手段检测线程是否已经终止执行
8. 对象终结规则
一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始
volatile 特点:可见 有序 不能保证原子!!!!!!!
volatile 的内存语义:
volatile 为什么可以保证可见性和有序性? --------- 内存屏障 Memory Barrier
内存屏障是什么?
四大屏障:
happens-before 之 volatile 规则
volatile 保证不同线程对某个变量完成操作之后结果及时可见,即该共享变量一旦改变所有线程立即可见
volatile 变量的读写过程:
volatile 不能保证原子性!!!!!!!
指令重排序
具体的内存屏障插入规则:
volatile 最佳使用场景:
单一赋值可以,but 复合运算赋值不可以(i++ 之类)
volatile int a = 10;
volatile boolean flag = true;
状态标志判断业务是否结束
开销较低的读、写锁策略
DCL 双端锁的发布
单例模式应用,传统的双重检查单例模式,在多线程的情况下,有可能会发生变量刚初始化 但是没复制就被拿去使用的情况(实例化一个对象的时候分多个步骤执行,包括分配内存空间、初始化对象、将对象指向分配的内存空间,而某些编译器为了性能原因会将第二步和第三步进行重排序,这样某个线程就又可能会获得一个未完全初始化的对象),使用 volatile 修饰的变量可以防止这种情况发生
volatile 小结: