什么是JMM模型?
- 概念:Java 内存模型,Java Memory Model,是一种抽象概念或规范,并非实际存在
- 主要规范了各变量的访问方式
- JVM会为每个线程都创建一个工作内存(也可称为栈空间),用于存储私有数据
- 而Java内存模型中还规定所有变量都存储在主内存,主内存是所有线程都可以共享访问的内存区
- 但线程对变量的操作都必须在工作内存中完成,所以这中间会有一次内存拷贝和内存回写
- 工作内存为每个线程私有数据区,线程间互不可见,必须通过主内存才可完成线程间数据通信
- 当然,不管是工作内存数据还是主内存数据,实际都是存储在主内存中,也可能存储到CPU缓存或寄存器中
不同于JVM内存区模型
- 两个属于不同层次的概念
- JMM其实描述的是一组规则,围绕变量的原子性、有序性和可见性展开
- JVM内存区划分是围绕程序执行过程中的堆、栈、程序计数器、方法区等概念
- JMM描述的规则与JVM内存区有一些相似概念
- 其实两者都属于是抽象的规范,是对内存区域的一种概念上的区分,并不实际存在
主内存与工作内存
- 工作内存主要存储着主内存中部分数据的副本
- 由上可知,工作内存所存储的数据是不存在线程安全问题的,但主内存中Gong享的数据存在
- 举例:两个不同线程去操作同一个对象的同一个方法,都会将数据拷贝一份到自己的工作内存,操作完成后,将数据刷回到主内存
- JMM与硬件内存架构的联系,其实是一种相互交叉关系
JMM存在的必要性
- 两个线程同时对主存中的实例对象进行操作,可能诱发线程安全问题
- 举例:共享变量x,AB两个线程,都对x操作,A将x=2,B来读x,此时B读到的是1还是2?不确定
- 具体会是哪种情况发生,对应哪种结果?JVM定义了8大原子操作规范,成为两个内存之间的具体交互协议及实现细节
数据Sync八大原子操作
- lock,作用于主内存,将变量标记为独占状态
- unlock,作用于主内存,释放锁定变量,释放后才可被其他线程锁定
- read,作用于主内存,将值从主内存传输到工作内存
- load,作用于工作内存,把read过来的变量放入工作内存变量副本
- use,作用工作内存,将一个值传递给执行引擎
- assign,作用于工作内存,从执行引擎接Shou到的值赋给工作内存变量
- store,作用工作内存,把工作内存中的值传给主内存
- write,作用于工作内存,将store操作的变量值传递给主内存的变量
- JMM要求从主存复制到工作内存,或从工作内存sync到主存,必须按顺序执行相关操作,但没要求必须连续执行
可见性、原子性与有序性问题
- **原子性:**一个操作一旦开始,不可能被其他线程中断
- **可见性:**一个线程修改了某个变量,另外线程是否可立马知道这个修改
- **有序性:**对于单线程而言,本线程内可视为有序执行没毛病,但多线程环境可能出现乱序现象,因为指令重排机制的存在
单看一个线程内,所有操作可视为有序,若是多线程环境下,一个线程观察另一个线程,所有操作可能都是无序的,主要原因为指令重排现象加工作内存与主内存Sync延迟现象所致
如何解决?
- 原子性:JVM除对基本数据类型读写操作实现了原子性外,可使用synchronized和Lock来实现,因为其能保证任意时刻只有一个线程访问该代码块
- 可见性:volatile关键字,可保证其共享变量的可见性,即为当某个线程修改了该变量,其他线程可立即感知到
- 有序性:volatile可一定程度的保证有序性,也可通过synchronized 或 Lock来保证
volatile 关键字
- JVM提供的轻量级Sync机制,保证共享变量对所有线程总是可见的
- 原理可简单理解为:变量每次在被访问时,都是从主内存中读改变量,而一旦发生变化,则强迫将最新的值刷到主存
- 并且会禁止指令重排机制发生,关于这个,可以去深入了解下**【内存屏障】**概念
- 无法保证原子性,例如在某个方法里对i++,多线程执行时可能存在线程安全问题,具体可以深入了解其原因
扩展了解DCL(单例的双重检测)
/**
* @author qinchen
* @date 2021/7/8 16:38
* @description 单例模式下的双检锁学知识习
*/
public class DoubleCheckLock {
private static DoubleCheckLock instance;
public static DoubleCheckLock getInstance() {
// 第一次检测
if(instance == null) {
// Sync代码块
synchronized (DoubleCheckLock.class) {
// 第二次检测
if(instance == null) {
// 在多线程环境下,依然可能出现问题
instance = new DoubleCheckLock();
}
}
}
return instance;
}
}