Java内存模型(Java Memory Model,简称JMM)是Java虚拟机规范中定义的一套规则,用于描述Java程序中的内存一致性模型。JMM规定了程序中各种变量(线程共享变量)的访问规则,以及在并发环境下如何保证内存的可见性、有序性和原子性。理解JMM对于正确地编写并发程序至关重要。
Java内存模型的关键概念
1. 主内存与工作内存
- 主内存(Main Memory):JMM的概念化内存区域,所有线程共享的内存区域,存储了所有线程可见的变量(实例字段、静态字段和数组元素)的最终值。
- 工作内存(Working Memory):每个线程拥有的私有内存区域,用于存储该线程使用的变量的副本。
2. 内存操作
- lock(锁定):作用于主内存变量,把一个变量标识为一条线程独占的状态。
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
- read(读取):作用于主内存变量,把一个变量的值从主内存转移到线程的工作内存。
- load(载入):作用于工作内存变量,把read操作从主内存得到的变量值放入工作内存的变量副本中。
- use(使用):作用于工作内存变量,把工作内存中的一个变量值传送给执行引擎。
- assign(赋值):作用于工作内存变量,把从执行引擎接收到的值赋给工作内存的变量。
- store(存储):作用于工作内存变量,把工作内存中的一个变量的值传送到主内存中。
- write(写入):作用于主内存变量,它将store操作从工作内存中得到的变量的值放入主内存的变量中。
3. 内存可见性
内存可见性是指当一个线程修改了共享变量的值,其他线程能够立即看到这个修改。JMM通过在变量修改后将新值刷新回主内存,在变量读取前从主内存刷新变量值,来实现可见性。
4. 原子性
原子性是指一个操作要么全部完成,要么全部不完成。在JMM中,基本数据类型的变量读取和写入是原子操作,而复合操作(如自增)需要通过synchronized等同步机制来保证原子性。
5. 有序性
有序性指的是程序执行的顺序按照代码的先后顺序执行。然而,为了提高性能,编译器和处理器常常会对指令进行重排序。JMM通过在关键代码处插入内存屏障来禁止特定类型的指令重排序,从而保证有序性。
Java内存模型的规则
JMM定义了一系列规则来保证线程之间的正确交互:
- 不允许读/写重排序:读取操作(read)和写入操作(write)之间不能重排序。
- 不允许写/写重排序:两个写入操作(write)之间不能重排序。
- 锁规则:如果一个锁被释放,那么另一个线程可以获取这个锁。锁规则保证了锁的获取和释放操作不会被重排序。
- volatile变量规则:对volatile变量的写入会使其随后的写入操作发生之前,对volatile变量的读取会使其前面的读取操作发生之后。
- happens-before规则:如果一个操作happens-before另一个操作,那么第一个操作的结果对第二个操作可见,并且第一个操作按顺序发生在第二个操作之前。
volatile关键字
volatile
关键字是JMM提供的一种轻量级的同步机制,它可以保证变量的可见性和有序性,但不保证原子性。当一个变量被声明为volatile
时,意味着每次对该变量的写入都会直接写入主内存,每次读取都是直接从主内存中读取。
synchronized关键字
synchronized
关键字可以用来保证原子性、可见性和有序性。它通过在变量上加锁来防止多个线程同时访问临界区。synchronized
关键字可以用在方法或代码块上。
示例代码
下面是一个使用volatile
关键字的简单示例:
public class VolatileExample {
private volatile boolean stop = false;
public void doWork() {
while (!stop) {
// 执行一些任务
}
}
public void stopWork() {
stop = true;
}
public static void main(String[] args) throws InterruptedException {
VolatileExample example = new VolatileExample();
Thread worker = new Thread(example::doWork);
worker.start();
Thread.sleep(1000); // 等待一段时间
example.stopWork(); // 设置停止标志
worker.join(); // 等待worker线程结束
}
}
在这个示例中,stop
变量被声明为volatile
,这意味着任何对它的写操作都会立即反映到主内存中,并且其他线程读取该变量时会直接从主内存中读取最新的值。
总结
Java内存模型是Java并发编程的基础,理解其核心概念和规则对于编写正确的并发程序至关重要。通过掌握JMM,开发人员可以更好地利用Java提供的并发工具,如volatile
和synchronized
,以及更高级的并发工具,如Atomic
类和Lock
接口,来构建高效且可靠的多线程应用程序。