在Java中,happens-before 规则是Java内存模型(JMM, Java Memory Model)的一部分,用于定义程序中操作之间的部分有序关系,确保多线程环境下的可见性和顺序性。
通过这些规则,程序员可以确定某些操作是否对其他线程可见以及它们发生的相对顺序。
Happens-Before 规则的主要目的
- 确保可见性:如果操作A happens-before 操作B,则操作A的结果对于操作B来说是可见的。
- 保证顺序性:虽然Java内存模型允许一定程度上的指令重排序来优化性能,但是happens-before规则限制了这种重排序,以确保程序逻辑的正确性。
常见的 Happens-Before 规则
以下是几种典型的happens-before关系:
- 程序顺序规则:
- 在单个线程内,每个操作都按照程序代码的书写顺序执行(即程序顺序),这意味着每个动作都happens-before于该线程后续的动作。
- 监视器锁规则(Monitor Lock Rule):
- 当一个线程释放一个锁时,它所进行的所有写入操作都happens-before另一个线程获取同一个锁后所做的读取操作。这保证了同步块或同步方法内的操作对其他获取同一锁的线程是可见的。
- volatile变量规则:
- 对
volatile
变量的写入操作happens-before任何后续对该变量的读取操作。这意味着所有对volatile
变量的写操作都会立即刷新到主内存,并且任何读取操作都会从主内存中读取最新的值,而不是缓存中的旧值。
- 对
- 线程启动规则(Thread Start Rule):
Thread.start()
调用happens-before由该线程执行的所有动作。这意味着主线程中start()
之前的所有初始化工作对新启动的线程都是可见的。
- 线程终止规则(Thread Termination Rule):
- 一个线程中的所有动作happens-before其他线程检测到该线程已经终止的动作(例如,通过
Thread.join()
方法返回)或者从Thread.isAlive()
方法返回false。
- 一个线程中的所有动作happens-before其他线程检测到该线程已经终止的动作(例如,通过
- 中断规则(Interruption Rule):
- 一个线程调用另一个线程的
interrupt()
方法happens-before被中断线程检测到中断发生的动作(如抛出InterruptedException
或调用isInterrupted()
)。
- 一个线程调用另一个线程的
- 终结器规则(Finalizer Rule):
- 对象的构造函数完成happens-before其
finalize()
方法开始。
- 对象的构造函数完成happens-before其
- 传递性(Transitivity):
- 如果操作A happens-before 操作B,而操作B happens-before 操作C,则操作A happens-before 操作C。
实际应用示例
考虑以下例子,展示了如何使用synchronized
关键字来确保happens-before关系:
public class SharedObject {
private int value = 0;
public synchronized void setValue(int value) {
this.value = value; // 写操作
}
public synchronized int getValue() {
return this.value; // 读操作
}
}
在这个例子中,由于setValue
和getValue
方法都被声明为synchronized
,根据监视器锁规则,所有对value
的写操作都happens-before任何后续的读操作,从而确保了一个线程对value
的修改对其他线程是可见的。
理解并正确应用happens-before规则是编写高效且正确的并发程序的关键。通过遵循这些规则,可以避免常见的并发问题,如数据竞争和不一致状态。
THE END