Java 的Object的 notify()底层实现原理
特性描述方法签名作用唤醒一个在对象上等待的线程依赖机制对象监视器(monitor)、wait set底层实现JVM 使用管理线程等待与唤醒调用限制必须在 synchronized 块内调用线程安全安全,但需注意条件变量的同步问题典型用途多线程协作、生产者-消费者模型、任务调度等场景理解notify()的底层实现有助于更好地掌握 Java 并发编程的核心机制,也能帮助开发者写出更健壮、高效的并发程序
@IntrinsicCandidate
public final native void notify();
是 Java 中 Object
类的一个 native 方法,用于实现线程间的通信机制。它在 Java 多线程编程中扮演重要角色,是 wait/notify
机制的一部分。
🔍 方法说明
@IntrinsicCandidate
public final native void notify();
- 作用:唤醒一个正在等待该对象监视器(monitor)的线程。
- 访问权限:
final
表示不能被重写;native
表示由 JVM 底层实现。 - 使用前提:必须在 synchronized 块或方法中调用,否则抛出
IllegalMonitorStateException
。 - 唤醒行为:从所有因调用
wait()
而阻塞在该对象上的线程中随机选择一个进行唤醒(不是按优先级或顺序)。
🧠 notify()
的语义理解
Java 中的 notify()
和 wait()
都基于 对象的监视器(monitor) 实现:
- 每个 Java 对象都有一个与之关联的 监视器锁(monitor lock)。
- 当多个线程调用
wait()
时,它们会进入该对象的 等待集合(wait set)。 - 调用
notify()
会从这个等待集合中唤醒一个线程(不保证顺序),使其可以重新竞争对象的锁。
⚙️ 底层实现原理
1. JVM 内部结构
JVM 使用以下核心机制来支持 notify()
:
组件 | 描述 |
---|---|
ObjectMonitor | 每个 Java 对象内部有一个 monitor 结构,负责管理锁和线程等待/通知 |
Wait Set | 存放调用了 wait() 的线程 |
Entry Set / Contentions List | 管理试图获取锁但未成功的线程 |
2. C++ 层面的实现(HotSpot JVM)
在 OpenJDK 的 HotSpot 虚拟机中,notify()
的底层实现在 C++ 的 objectMonitor.cpp
文件中。
核心流程如下:
- 检查当前线程是否持有对象锁
- 如果没有持有锁,抛出
IllegalMonitorStateException
- 如果没有持有锁,抛出
- 查找是否有线程在 Wait Set 中等待
- 如果有,则从中移除一个线程,并将其放入 Entry Set 或直接唤醒以重新竞争锁
- 唤醒线程
- 使用操作系统提供的线程调度机制(如 Linux 上的 futex、Windows 上的 Event/Condition Variable)进行唤醒操作
示例伪代码(简化版):
void ObjectMonitor::notify(TRAPS) {
if (current_thread != owner()) {
THROW(vmSymbols::java_lang_IllegalMonitorStateException());
}
if (wait_set_head == NULL) {
return; // 没有线程在等待
}
Thread* notified = remove_from_wait_set(wait_set_head);
add_to_entry_set(notified); // 放入 entry set 等待锁
}
📦 @IntrinsicCandidate
注解的作用
@IntrinsicCandidate
是 JDK 9 引入的一个注解,表示该方法可能被 JVM 内联优化(intrinsic),即在某些情况下 JVM 可能不会真正调用 native 函数,而是直接在 JIT 编译时替换为更高效的机器指令。
但这并不改变其语义和功能。
🧪 使用示例
public class NotifyExample {
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
System.out.println("Thread waiting...");
try {
lock.wait(); // 等待通知
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread awakened!");
}
}).start();
try {
Thread.sleep(1000); // 主线程等待子线程进入 wait
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
lock.notify(); // 唤醒一个等待线程
}
}
}
📌 注意事项
项目 | 说明 |
---|---|
必须配合 synchronized 使用 | 否则抛出异常 |
无法控制唤醒哪个线程 | JVM 随机选择一个线程唤醒 |
唤醒后仍需重新获取锁 | 即使被唤醒,线程也必须重新竞争 monitor 锁才能继续执行 |
不能保证立即执行 | 被唤醒的线程只是进入可运行状态,何时调度取决于线程调度器 |
避免虚假唤醒 | 推荐使用 while 循环判断条件,而不是 if 判断 |
✅ 总结
特性 | 描述 |
---|---|
方法签名 | public final native void notify(); |
作用 | 唤醒一个在对象上等待的线程 |
依赖机制 | 对象监视器(monitor)、wait set |
底层实现 | JVM 使用 ObjectMonitor 管理线程等待与唤醒 |
调用限制 | 必须在 synchronized 块内调用 |
线程安全 | 安全,但需注意条件变量的同步问题 |
典型用途 | 多线程协作、生产者-消费者模型、任务调度等场景 |
理解 notify()
的底层实现有助于更好地掌握 Java 并发编程的核心机制,也能帮助开发者写出更健壮、高效的并发程序。如果你对 JVM 内部机制感兴趣,可以进一步研究 objectMonitor.hpp
和 objectMonitor.cpp
源码。
更多推荐
所有评论(0)