一、程序顺序规则(单线程顺序性)
规则
同一线程中的操作,按代码顺序执行(但不禁止编译器和 CPU 的指令重排序,只要结果一致)。
示例
int a = 1; // 操作1
int b = 2; // 操作2
int c = a + b; // 操作3
happens-before关系:
操作1 → 操作2 → 操作3(尽管实际执行时操作1和2可能重排序,但结果必须保证c=3)。
二、volatile 变量规则
规则
对 volatile 变量的写操作 happens-before 后续的读操作,表示存在数据依赖时的可见性和有序性。
示例
public class VolatileExample {
private volatile boolean flag = false;
private int value = 0;
public void writer() {
value = 42; // 操作1
flag = true; // 操作2(volatile 写)
}
public void reader() {
if (flag) { // 操作3(volatile 读)
System.out.println(value); // 输出 42
}
}
}
happens-before关系:
操作1 → 操作2(程序顺序规则)
操作2 → 操作3(volatile规则)
操作3 → 操作4(程序顺序规则)- 结果保证:线程 B 在读到
flag=true时,一定能看到value=42。
三、锁规则(synchronized)
规则
解锁操作 happens-before 后续的加锁操作。
示例
public class LockExample {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) { // 加锁
count++; // 操作1
} // 解锁
}
public int getCount() {
synchronized (lock) { // 加锁
return count; // 操作2
} // 解锁
}
}
happens-before关系:
线程 A 的解锁 → 线程 B 的加锁- 结果保证:线程 B 调用
getCount()时,能看到线程 A 对count的修改。
四、线程启动规则
规则
Thread.start() 前的操作 happens-before 新线程中的所有操作。
示例
public class ThreadStartExample {
static int data = 0;
public static void main(String[] args) {
data = 100; // 操作1
Thread thread = new Thread(() -> {
System.out.println(data); // 输出 100(操作2)
});
thread.start(); // 操作3
}
}
happens-before关系:
操作1 → 操作3 → 操作2- 结果保证:新线程能看到
data=100。
五、线程终止规则
规则
线程中的所有操作 happens-before 其他线程检测到该线程终止(如 Thread.join())。
示例
public class ThreadJoinExample {
static int result = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
result = 42; // 操作1
});
thread.start(); // 操作2
thread.join(); // 操作3
System.out.println(result); // 输出 42(操作4)
}
}
happens-before关系:
操作1 → 操作3 → 操作4- 结果保证:主线程在
join()后能看到result=42。
六、传递性规则
规则
若 A happens-before B,且 B happens-before C,则 A happens-before C。
示例
public class TransitivityExample {
private int x = 0;
private volatile boolean flag = false;
public void writer() {
x = 1; // 操作1
flag = true; // 操作2(volatile 写)
}
public void reader() {
if (flag) { // 操作3(volatile 读)
System.out.println(x); // 输出 1(操作4)
}
}
}
happens-before关系:
操作1 → 操作2(程序顺序规则)
操作2 → 操作3(volatile规则)
操作3 → 操作4(程序顺序规则)
因此,操作1 → 操作4(传递性)- 结果保证:线程 B 看到
flag=true时,一定能看到x=1。
七、happens-before 的实际意义
- 简化多线程编程:开发者无需关心底层指令重排序和缓存细节,只需遵循
happens-before规则。 - 指导同步机制选择:
- 使用
volatile保证可见性和有序性。 - 使用锁(
synchronized、Lock)保证原子性和复合操作的顺序性。
- 使用
- 避免数据竞争:通过规则组合,确保多线程访问共享资源的安全。
总结
| 规则 | 典型场景 | 保证内容 |
|---|---|---|
| 程序顺序规则 | 单线程代码顺序 | 单线程执行结果正确 |
volatile 规则 | 状态标志、双重检查锁 | 可见性、禁止重排序 |
| 锁规则 | 同步代码块 | 互斥访问、可见性 |
| 线程启动/终止规则 | Thread.start()/Thread.join() | 跨线程操作可见性 |
| 传递性规则 | 多规则组合 | 跨操作链的可见性和有序性 |
理解 happens-before 是编写正确并发程序的关键!