概述
JMM(Java Memory Model,Java 内存模型)解决的核心问题是:多线程同时读写共享数据时,Java 如何规定可见性、执行顺序和同步语义。它让不同 CPU、操作系统和 JVM 对并发程序遵守同一组合法行为约束,但并不保证存在数据竞争的程序每次都得到相同结果。
JMM 不是一种具体的内存结构,也不是 JVM 运行时数据区,而是一套并发语义规范。
如果需要先理解原子性、可见性和有序性的区别,可以阅读 Java 并发问题:原子性、可见性与有序性。
happens-before
规则
happens-before 是 JMM 描述跨线程可见性与顺序保证的核心关系。
如果操作 A happens-before 操作 B,那么 A 的结果必须对 B 可见,并且 A 的执行顺序先于 B。它描述的是内存模型保证的偏序关系,不等同于简单的墙上时钟先后,也不意味着 JVM 不能进行保持该关系的优化或重排序。
常用规则:
- 程序顺序规则:同一线程内,前面的操作 happens-before 后面的操作
- 锁规则:对一把锁的解锁,happens-before 后续对同一把锁的加锁。
- volatile 规则:对一个
volatile变量的写,happens-before 后续对这个变量的读。 - 线程启动规则:调用
Thread.start()之前的操作,happens-before 新线程中的操作。 - 线程终止规则:线程中的所有操作,happens-before 其他线程从
Thread.join()成功返回。 - 传递性:如果 A happens-before B,且 B happens-before C,那么 A happens-before C。
示例
class Data {
int value = 0;
volatile boolean ready = false;
void write() {
value = 42;
ready = true;
}
int read() {
if (ready) {
return value;
}
return -1;
}
}
如果线程 A 执行 write(),线程 B 之后读到 ready == true,那么线程 B 也必须看到 value == 42。
链条是:
- 在线程 A 内,
value = 42happens-beforeready = true。 ready = true是 volatile 写,happens-before 线程 B 对ready的 volatile 读。- 在线程 B 内,读
readyhappens-before 读value。 - 根据传递性,
value = 42对线程 B 可见。