Java内存模型(Java Memory Model,JMM)是Java程序在多线程环境下的内存交互规则的规范,JMM定义了线程之间如何进行内存访问和交互
。其中的关键概念包括主内存
和工作内存
。
- 主内存:所有线程
共享的内存区域
,包含所有的共享变量。 - 工作内存:
每个线程
独立拥有的内存区域,其中包含了该线程使用到的变量的副本
。
其中JMM通过以下规则来确保多线程环境下的内存一致性
- 所有的
共享变量
存储在主内存
中,每个线程都有自己的工作内存
。 - 线程对共享变量的操作必须在
自己的工作内存中
进行对变量副本
的操作,不能直接读写主内存中的变量。 - 线程对共享变量的操作必须遵循特定的规则,如volatile关键字、synchronized关键字等,以保证
内存可见性、原子性和有序性
。
下面介绍一下什么是内存可见性、原子性和有序性。
内存可见性
: 指的是当一个线程修改了共享变量的值后,其他线程能够立即看到
这个修改。原子性
: 是指一个操作是不可中断的,要么全部执行成功
,要么全部不执行
。有序性
: 是指程序执行的顺序按照代码的先后顺序执行
,不会乱序执行。
下面使用一段代码演示一下内存可见性:
public class MemoryVisibilityExample {
private static boolean flag = false;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
flag = true;
System.out.println("thread sets true");
});
Thread thread2 = new Thread(() -> {
while (!flag) {
// Spin until flag becomes true
}
System.out.println("thread sees true");
});
writerThread.start();
readerThread.start();
}
}
thread1 设置了flag为true,而thread2 会不断地检查flag是否为true,如果没有使用合适的同步机制,可能会出现内存可见性问题,thread2 可能永远无法看到thread1 设置的flag为true,导致程序陷入死循环。
为了解决thread2,可以使用volatile关键字来保证flag的可见性,即将flag声明为volatile类型:
private static volatile boolean flag = false;
这样就可以确保在一个线程修改了flag的值后,其他线程能够立即看到最新的值
,从而避免了内存可见性问题。