JAVA内存模型(JMM)详解
什么是JMM
JMM 是 Java Memory Model 的缩写,即 Java 内存模型,是一种制定了共享内存系统中多线程访问规则的抽象规范。它规定了 JVM 中各个线程之间的共享变量存储在主内存中,每个线程都有自己的工作内存和虚拟机栈,变量值的更改仅在工作内存中进行,需要同步到主内存中才能被其他线程看到。
JMM 可以保证 Java 多线程程序在并发执行时的可见性(Visibility)、有序性(Ordering)和原子性(Atomicity)。
JMM 规定的内存模型
在 JMM 的规范中,分别定义了以下不同的内存区域:
- 主内存:所有变量的存储位置,是所有线程共享的。
- 工作内存:每个线程中会包含有一个工作内存,线程的操作即在工作内存中进行。
- 指令集:CPU 中的指令。
- CAS 操作:原子操作的一种,即比较并交换。
JMM 同时也规定了以下行为:
- 线程间通信(Lock,volatile 变量,synchronized,AtomicInteger 等):保证可见性、有序性和原子性。
- happens-before 关系:保证程序运行的顺序。
- 内存屏障(Memory Barriers):强制工作内存和主内存之间的通信。
JMM 的实例
以下是两个使用 JMM 的实例:
示例1:内存可见性
下面是一个简单的多线程示例,其中线程 A 在运行时修改了共享变量 count 的值,而线程 B 需要使用这个 count 值。如果不使用 JMM,线程 B 很大概率无法读取到线程 A 修改后的值。
public class VisibilityDemo {
private volatile static int count = 0;
public static void main(String[] args) {
new Thread(() -> {
while (count == 0) {}
System.out.println(Thread.currentThread().getName() + " exit");
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
count = 1;
System.out.println(Thread.currentThread().getName() + " set count to 1");
}
}
使用 volatile
关键字可以让程序使用 JMM 模型:
private volatile static int count = 0;
输出结果为:
Thread-0 exit
main set count to 1
结论:使用 volatile
可以防止写操作前后的指令重排,保障了内存可见性。
示例2:原子性
明显的例子是多线程下的计数器,如果不是原子性的自增会导致不准确的结果,甚至程序奔溃。使用 JMM 的 AtomicInteger 可以将其变得更安全。
public class AtomicDemo{
static AtomicInteger ai = new AtomicInteger(1);
public static void main(String[] args){
new Thread(() ->{
int i = ai.getAndSet(2);
System.out.println("Thread1: ai = " + ai + ", i = " + i);
}).start();
new Thread(() ->{
int i = ai.getAndIncrement();
System.out.println("Thread2: ai = " + ai + ", i = " + i);
}).start();
}
}
输出结果:
Thread1: ai = 2, i = 1
Thread2: ai = 3, i = 2
结论:使用 AtomicInteger
可以保证一个自增操作的原子性。
结论
JMM 的核心思想是通过将共享变量存储在主内存中,为每个线程创建一个工作内存,严格限制对共享变量的访问,保证并发环境下多线程的安全性。JMM 的实现方式和规范可以保证多线程程序的可见性、有序性和原子性要求,是 Java 多线程程序实现的重要基础。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JAVA内存模型(JMM)详解 - Python技术站