目录
1、Java的内存模型(JMM)介绍
JMM核心定义和作用
Java内存模型(Java Memory Model,JMM)是Java虚拟机规范中定义的一种抽象概念,它规定了多线程环境下,线程如何与内存进行交互。
JMM的核心作用:
-
定义程序中各个变量的访问规则
-
确保多线程程序的可见性 、有序性 和原子性
-
屏蔽不同硬件平台和操作系统的内存访问差异
JVM和JMM的区别
说到JMM,我们不得不提到它经常被人所搞混淆的另一个概念JVM,我们用一张表来直观表现出它们的区别。
|-------|--------------|--------------|
| | JVM内存结构 | Java内存模型 |
| 核心关注点 | 数据存储的物理/逻辑分区 | 线程与内存的交互规则 |
| 内容 | 堆、栈、方法区等内存区域 | 主内存、工作内存抽象概念 |
| 目的 | 内存分配与管理 | 多线程内存可见性控制 |
2.JMM核心概念
主内存和工作内存
-
主内存:所有线程共享的内存区域,存储所有实例字段、静态字段和数组元素
-
工作内存:每个线程私有的内存空间,存储线程使用变量的副本
当某个线程需要使用到内存中的变量时,他会先从主内存中复制一份该变量的副本到自己的工作内存当中,使用完后再将该变量写入主内存的共享内存中。

内存间的交互操作
-
lock/unlock:作用于主内存,标识变量为线程独占状态
-
read/load:从主内存读取变量到工作内存
-
use/assign:工作内存中使用和赋值操作
-
store/write:将工作内存变量写回主内存
内存三大特性
原子性
核心概念:原子性 指一个操作或一系列操作要么全部执行成功 ,要么全部不执行,不会出现执行到一半的状态。
java
// 原子操作示例
int x = 10; // 原子的:一次性赋值
boolean flag = true; // 原子的
// 非原子操作示例
int i = 0;
i++; // 非原子的,实际包含3个步骤:
// 1.读取i的值到寄存器
// 2.寄存器值加1
// 3.写回内存
可见性
核心概念:可见性 指当一个线程修改了共享变量的值,其他线程能够立即看到修改后的值。
java
private boolean running = true; // 主内存中的变量
public void run() {
while (running) { // 工作内存中的副本
// 看不到running被改为false
}
}
public void stop() {
running = false; // 修改主内存,但工作内存可能没更新
}
有序性
核心概念:有序性 指程序执行的顺序按照代码的先后顺序 执行。但在多线程或优化环境下,指令可能被重排序。
重排序原因:
-
编译器优化重排序
-
处理器指令级并行重排序
-
内存系统重排序
java
private int x = 0;
private boolean flag = false;
// 线程1执行
public void writer() {
x = 42; // 1
flag = true; // 2 可能被重排到1之前!
}
// 线程2执行
public void reader() {
if (flag) { // 3
System.out.println(x); // 可能输出0而不是42!
}
}
3.Happens-Before规则
Happens-Before规则介绍
Happens-Before 是JMM的核心概念,它定义了两个操作之间的偏序关系:
-
如果操作A happens-before 操作B
-
那么A的所有写操作 对B的读操作 都是可见的
有点难看懂,我们用一个简单的例子就能快速理解
java
// 核心:happens-before ≠ 时间先后
int x = 0;
int y = 0;
// 时间上:先执行1,后执行2
x = 1; // 1
y = x + 1; // 2
// 逻辑上:1 happens-before 2
// 所以2一定能看到1写入的值
A happens-before B 翻译过来就是:A对B可见。
六大happens-before规则
- 程序次序规则:**线程内,**按照程序代码顺序,前面的操作happens-before后面的操作。
- 监视器锁规则:对一个锁的解锁 操作happens-before后续对这个锁的加锁操作。
- volatile变量规则:对一个volatile变量的写 操作happens-before后续对这个变量的读操作。
- 线程启动规则:Thread对象的start() 方法调用happens-before该线程的每一个动作。
- 线程终止规则:线程中的所有操作happens-before其他线程检测到该线程已经终止
- 传递性规则:如果A happens-before B,且B happens-before C,那么A happens-before C
4.volatile关键字
核心概述: volatile 是一个重要的关键字,用于告知编译器某个变量的值可能会被程序外部的因素意外修改,从而避免编译器对该变量进行优化。它的主要作用是确保每次访问变量时都从内存中读取最新的值,而不是使用寄存器中的缓存值。
volatile提供了两大保证:
- 可见性:修改立即对所有线程可见
- 有序性:禁止指令重排序
java
private volatile boolean flag = false;
private int count = 0;
public void writer() {
count = 42; // 普通写操作
flag = true; // volatile写操作
}
public void reader() {
if (flag) { // volatile读操作
// 这里一定能看到count=42
System.out.println(count);
}
}
5.JMM的常见误区
volatile无法保证原子性
volatile可以保证可见性和有序性,但和synchronized不一样,不能保证原子性
java
// 错误:以为volatile能保证原子性
volatile int count = 0;
count++; // 非原子操作
// 正确:使用原子类或同步
AtomicInteger atomicCount = new AtomicInteger(0);
atomicCount.incrementAndGet();
指令重排序的陷阱
java
// 可能由于重排序导致问题
int a = 0;
boolean flag = false;
// 线程1
a = 1; // 1
flag = true; // 2 可能重排到1之前
// 线程2
if (flag) {
System.out.println(a); // 可能输出0
}
此时我们需对flag使用volatile关键字修饰即可保证在a赋值后再执行flag=true操作。
制作不易,如果对你有帮助请**点赞,评论,收藏,**感谢大家的支持
