13.JMM和Volatile
JMM
对Volattile的理解
Volatile是java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
可见性怎么保证,JMM
JMM java内存模型,不存在的东西,概念
关于JMm的一些同步的约定
- 线程解锁前,必须把共享变量立刻刷回到主存
- 线程加锁前,必须读取主存中的最新值到工作内存
- 加锁和解锁是同一把锁
8种操作
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
对八种操作的规则
- 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
- 对一个变量进行unlock操作之前,必须把此变量同步回主内存
问题:程序不知道主内存的值已经修改过了?怎么办?Volatile
//num已经被main线程修改为1,刷回了主内存,但new thread读不到,num还是0,所以程序就不会停止
public class VolatileTest {
private static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (num == 0) {
// System.out.println(num);
}
}).start();
TimeUnit.SECONDS.sleep(1);
num=1;
System.out.println(num);
}
}
Volatile
保证可见性,不保证原子性,避免指令重排
保证可见性
public class VolatileTest {
private volatile static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (num == 0) {
}
}).start();
TimeUnit.SECONDS.sleep(1);
num=1;
System.out.println(num);
}
}
不保证原子性
原子性:不可分割
线程A在执行任务的时候,不能被打扰,不能被分割,要么同时成功,要么同时失败
测试volatile不保证原子性
/*
* volatile 不保证原子性
* */
public class VolatileAtomicTest {
private static int a = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000; j++) {
//add1();
add();
}
}).start();
}
while (Thread.activeCount() > 2) {//main和gc,大于2表示还有new Thread在运行。这个能跑完说明线程一定跑完了
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "==>" + a);//!=2000
}
public static void add() {
a++;//不是原子操作
}
}
分析class字节码文件
add方法有如下操作
1、获取i
2、i自增
3、回写
D:\tools\juc\target\classes\volatiletest>javap -c VolatileAtomicTest.class
怎么保证原子性
-
如果不加lock和synchronized,怎么保证原子性?
-
原子操作类:
java.util.concurrent.atomic
原子类的底层都是和操作系统相关,在内存中修改值!Unsafe类一个很特殊的存在
/*
* 使用原子操作类保证原子性java.util.concurrent.atomic
* */
public class VolatileAtomicTest1 {
private static volatile AtomicInteger a = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {//main和gc,大于2表示还有new Thread在运行。这个能跑完说明线程一定跑完了
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "==>" + a);//Integer保证了原子性
}
public static void add() {
//getAndIncrement相当于a++
//incrementAndGet相当于++a
a.getAndIncrement();//CAS
}
}
指令重排
什么是指令重排:你写的程序,计算机并不是按你写的那样执行的
源代码--->编译器优化的重排--->指令并行也可能会重排--->内存系统也会重排--->执行
处理器在进行指令重排的时候,会考虑:数据之间的依赖性!
int x=1; //1
int y=2; //2
x=x+5; //3
y=x*x; //4
我们期望的:1234 但是执行时可能是: 2134 1324,这些都是正确的
4123是不可能的
可能造成影响的结果:ab xy初始值都是0
线程A | 线程B |
---|---|
x = a | y = b |
b = 1 | a = 2 |
正常结果:x = 0,y = 0,但是由于可能指令重排,线程A和B重排后顺序可能如下
线程A | 线程B |
---|---|
b = 1 | a = 2 |
x = a | y = b |
指令重排导致的非正常结果:x = 2,y = 1
volatile可以避免指令重排
内存屏障,就是CPU指令。作用:
-
保证特定的操作的执行顺序
-
可以保证某些变量的内存可见性
Volatile是可以保证可见性,不能保证原子性,由于内存屏障,可以避免指令重排
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY