Java Happens-Before 关系详解及案例
Happens-Before 是 Java 内存模型(JMM)中最重要的概念之一,它定义了操作之间的内存可见性保证,我将通过多个案例来详细说明。

什么是 Happens-Before?
Happens-Before 关系保证:如果操作 A Happens-Before 操作 B,A 所做的修改对 B 可见,且 A 的执行顺序在 B 之前。
Java 中的 Happens-Before 规则
/**
* Happens-Before 的 8 种基本规则
*/
public class HappensBeforeRules {
// 规则1:程序顺序规则
// 同一个线程中的每个操作都 happens-before 于该线程中后续的任意操作
// 规则2:监视器锁规则
// 对一个锁的解锁 happens-before 于随后对这个锁的加锁
// 规则3:volatile变量规则
// 对一个 volatile 域的写 happens-before 于任意后续对这个 volatile 域的读
// 规则4:传递性
// A happens-before B,且 B happens-before C,A happens-before C
// 规则5:线程启动规则
// Thread.start() happens-before 于该线程中的任意操作
// 规则6:线程终止规则
// 线程中的所有操作 happens-before 于其他线程检测到该线程终止
// 规则7:中断规则
// 线程的 interrupt() 调用 happens-before 于被中断线程检测到中断
// 规则8:对象终结规则
// 对象的构造函数结束 happens-before 于 finalize() 方法开始
}
具体案例演示
案例1:volatile 变量的 Happens-Before
public class VolatileExample {
private volatile boolean flag = false;
private int value = 0;
/**
* 问题:这个示例中,主线程写入 value 和 flag,
* 读线程能否看到主线程对 value 的修改?
*/
public void volatileDemo() {
// 线程A:写入数据
Thread writer = new Thread(() -> {
value = 42; // 1. 普通写
flag = true; // 2. volatile写
});
// 线程B:读取数据
Thread reader = new Thread(() -> {
while (!flag) { // 3. volatile读
// 等待
}
System.out.println("value = " + value); // 4. 普通读
});
writer.start();
reader.start();
/**
* Happens-Before 分析:
* - 操作1 happens-before 操作2(程序顺序规则)
* - 操作2 happens-before 操作3(volatile规则)
* - 操作3 happens-before 操作4(程序顺序规则)
* - 通过传递性,操作1 happens-before 操作4
* - 所以线程B一定能看到 value = 42
*/
}
}
案例2:synchronized 的 Happens-Before
public class SynchronizedExample {
private int count = 0;
private final Object lock = new Object();
/**
* synchronized 通过锁的获取和释放建立 Happens-Before 关系
*/
public void synchronizedDemo() {
// 线程A:写入数据
new Thread(() -> {
synchronized (lock) {
count = 100; // 1. 在锁内写入
} // 2. 释放锁
}).start();
// 线程B:读取数据
new Thread(() -> {
synchronized (lock) { // 3. 获取锁
// 4. 读取数据,能够看到线程A的修改
System.out.println("count = " + count);
}
}).start();
/**
* Happens-Before 分析:
* - 操作1 happens-before 操作2(程序顺序规则)
* - 操作2 happens-before 操作3(监视器锁规则:解锁happens-before加锁)
* - 操作3 happens-before 操作4(程序顺序规则)
* - 传递性:操作1 happens-before 操作4
*/
}
}
案例3:线程启动的 Happens-Before
public class ThreadStartExample {
private int data = 0;
private boolean initialized = false;
public void threadStartDemo() {
data = 88; // 1. 主线程写入数据
initialized = true; // 2. 设置标志
// 3. 启动新线程
Thread worker = new Thread(() -> {
// 4. 新线程中读取数据
// 通过线程启动规则,一定能看到主线程对 data 和 initialized 的修改
System.out.println("data = " + data);
System.out.println("initialized = " + initialized);
});
worker.start(); // 触发 Happens-Before
/**
* 分析:
* - 操作1、2 在 start() 之前
* - start() happens-before 新线程中的任何操作
* - 所以操作4一定能看到操作1、2的结果
*/
}
}
案例4:正确使用 Happens-Before 的示例
public class CorrectUsageExample {
private volatile boolean running = true;
private int taskCount = 0;
/**
* 实际应用:优雅停机
*/
public void gracefulShutdownDemo() {
Thread worker = new Thread(() -> {
while (running) { // volatile 读
// 执行任务
System.out.println("执行任务 " + (++taskCount));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("线程停止");
});
worker.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 主线程设置运行标志
running = false; // volatile 写
/**
* 通过 volatile 的 Happens-Before 关系,
* worker 线程能够看到 running = false 的修改
*/
}
}
案例5:利用传递性构建 Happens-Before
public class TransitivityExample {
private int a = 0;
private boolean flag = false;
/**
* 使用多个同步机制构建 Happens-Before 链
*/
public void transitivityDemo() {
Thread producer = new Thread(() -> {
synchronized (this) {
a = 10; // 1. 写入普通变量
flag = true; // 2. 写入标志
} // 3. 释放锁
});
Thread consumer = new Thread(() -> {
synchronized (this) { // 4. 获取锁
if (flag) { // 5. 读取标志
int value = a; // 6. 读取普通变量
System.out.println("通过传递性看到 a = " + value);
}
}
});
producer.start();
consumer.start();
/**
* Happens-Before 链:
* 1 -> 2 -> 3(程序顺序)
* 3 -> 4(监视器锁规则)
* 4 -> 5 -> 6(程序顺序)
* 传递性:1 happens-before 6
*/
}
}
破坏 Happens-Before 的错误示例
public class WrongExample {
private int value = 0;
private boolean ready = false;
/**
* 错误示例:没有正确建立 Happens-Before 关系
*/
public void wrongDemo() {
// 线程A:写入数据
new Thread(() -> {
value = 42; // 1. 普通写
ready = true; // 2. 普通写(不是volatile)
}).start();
// 线程B:读取数据
new Thread(() -> {
while (!ready) { // 3. 可能永远看不到 ready 的变化
// busy wait
}
// 4. 即使看到 ready,也可能看不到 value 的最新值
System.out.println("value = " + value);
}).start();
/**
* 问题:
* - 没有 volatile,没有 synchronized
* - 线程B可能永远看不到 ready = true
* - 即使看到 ready,也可能看到 value = 0
* - 这里没有建立任何 Happens-Before 关系
*
* 修正方法:
* - 将 ready 声明为 volatile
* - 或使用 synchronized 块
* - 或使用 AtomicBoolean
*/
}
// 正确的修正版本
public void correctDemo() {
Thread writer = new Thread(() -> {
value = 42;
// 使用 synchronized 确保可见性
synchronized (this) {
ready = true;
}
});
Thread reader = new Thread(() -> {
synchronized (this) {
while (!ready) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("value = " + value);
});
writer.start();
reader.start();
}
}
常见应用场景总结
public class HappensBeforeApplications {
/**
* 1. 双重检查锁定(DCL)- 需要 volatile
*/
private volatile static HappensBeforeApplications instance;
public static HappensBeforeApplications getInstance() {
if (instance == null) { // 第一次检查
synchronized (HappensBeforeApplications.class) {
if (instance == null) { // 第二次检查
instance = new HappensBeforeApplications();
}
}
}
return instance;
}
/**
* 2. 发布对象
*/
private final Map<String, String> config = new HashMap<>();
public void publishConfig() {
config.put("key", "value"); // 在发布前写入
// 确保正确发布,比如通过 volatile 引用或 synchronized
}
/**
* 3. 并发集合中的 Happens-Before
*/
public void concurrentCollections() {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// put 操作 happens-before 后续的 get 操作
map.put("count", 100);
// get 操作能看到 put 的结果
Integer count = map.get("count");
}
}
关键要点
-
Happens-Before 不是时间顺序:它强调的是内存可见性保证,不是物理时间上的先后。
-
传递性很重要:可以通过多个 Happens-Before 关系串联起来。
-
常见误区:
- 认为所有线程都能看到共享变量的修改
- 认为只需要加锁就能保证可见性
- 认为 volatile 能保证原子性
-
最佳实践:
- 优先使用 java.util.concurrent 包中的工具类
- 确保正确使用 volatile、synchronized
- 避免过度优化导致的内存可见性问题
通过理解和使用 Happens-Before 关系,我们可以更好地编写并发程序,避免由内存可见性导致的难以调试的问题。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播