并发2️⃣Java线程②wait/notify、park/unpark、状态转换
1、wait/notify
背景知识:synchronized、Monitor
1.1、API 介绍
- Object 类提供的方法。
- 必须成为对象 Monitor 的
Owner,才能调用这两个方法。
1.1.1、wait()
使 Monitor 的
Owner进入WaitSet
-
wait(long):最长等待指定毫秒数。
-
wait(long, int):最长等待指定毫秒数 + 1。
-
wait():无限期等待。
public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); } public final void wait() throws InterruptedException { wait(0); }
1.1.2、notify()
唤醒
WaitSet中的等待线程。
-
notify():随机唤醒一个。
-
notifyAll():唤醒所有。
public final native void notify(); public final native void notifyAll();
1.2、原理
-
Monitor 的
Owner的执行条件不满足时,调用 wait()- 主动释放锁。
- 进入
WaitSet,变成WAITING状态。
-
此时其它线程可以成为竞争锁,成为新的
Owner。 -
新的
Owner调用 notify() 或 notifyAll() 时,唤醒WaitSet中的线程。
Waitset 和 EntryList 的区别
| Waitset | EntryList | |
|---|---|---|
| 线程状态 | WAITING | BLOCKED |
| 进入条件 | 已获得锁的线程,由于条件不满足而调用 wait() |
尝试获得锁时,由于锁已被其它线程获取而阻塞 |
| 唤醒时机 | Owner 调用 notify() 或 notifyAll() |
Owner 释放锁 |
| 注意 | 唤醒后不会直接成为 Owner,而是进入 EntryList 竞争锁 | 若有多个等待线程,则竞争锁(非公平) |
2、park/unpark
2.1、API 介绍
JUC 包下 LockSupport 提供的方法。
// “暂停”当前线程
public static void park();
// “恢复”某个线程
public static void unpark(Thread thread);
2.1.1、park()
调用 park() 时,判断当前线程是否拥有许可。
- 若线程拥有许可,则消耗许可,不会禁用线程。
- 若线程没有许可,则禁用当前线程。
何时取消禁用?
- 其它线程调用
unpark(),当前线程解除阻塞。 - 其它线程调用
interrupt(),中断当前线程。
2.1.2、unpark()
调用 unpark(t),为指定线程 t 添加许可。
- 若线程 t 由于
park()而阻塞,则解除线程 t 的阻塞。 - 若线程 t 正在运行,则添加许可,线程 t 下次调用
park()时将不会阻塞。 - 若线程 t 未启动,此方法未必生效。
2.2、原理
每个线程独有一个 Parker 对象,由三部分组成
_counter:计数器,取值 0/1。_cond:条件变量,当满足条件时解除阻塞。-mutex
调用 park() 时:判断
_counter的值
- 若线程拥有许可(值为 1),则消耗许可(值重置为 0),不会禁用线程。
- 若线程没有许可(值为 0),则禁用当前线程,并进入
_cond阻塞。- 其它线程调用
unpark(),当前线程解除阻塞。 - 其它线程调用
interrupt(),中断当前线程。
- 其它线程调用
调用 unpark(t) 时:为指定线程 t 添加许可(
_counter设为 1)
- 若线程 t 由于
park()而阻塞(处于_cond),则设置_counter=1,解除线程 t 的阻塞并重置counter=0。 - 若线程 t 正在运行,则添加许可(
counter=1),线程 t 下次调用park()时将不会阻塞。
2.3、方法对比
| sleep() | wait() | park() | |
|---|---|---|---|
| API | Thread | Object | LockSupport |
| synchronized? | 不需要强制配合 synchronized 使用 | 只能在 synchronized 代码块中使用 | 不需要强制配合 synchronized 使用 |
| 释放对象锁? | ❌ | ✔ | ❌ |
3、线程状态转换

3.1、NEW → RUNNABLE
- 实例化线程对象时:线程状态为
NEW。 - 调用
start()时:线程状态NEW→RUNNABLE
3.2、RUNNABLE <--> 阻塞
阻塞状态细分
BLOCKED:等待 Monitor 锁。WAITING:无限期等待另一个线程执行完某个特定操作。TIMED_WAITING:在指定等待时间内,等待另一个线程执行完某个特定操作。
3.2.1、WAITING
不会被 CPU 分配时间片,无限期等待显式唤醒。
- join()
- wait()
- park()
join()
- 线程 t1 调用
t1.join():t1 进入 t2 对象的 Monitor 的 WaitSet,t1 状态RUNNABLE→WAITING。 - 唤醒:其它线程调用
t1.interrupt()、t2 运行结束:t1 退出 WaitSet,状态WAITING→RUNNABLE。
wait()
线程 t 执行到 synchronized(obj){}获取对象锁,成为 Monitor 的 Owner 后:
- 线程 t 调用
obj.wait():t 进入 WaitSet,状态RUNNABLE→WAITING。 - 唤醒:其它线程调用
obj.notify()、obj.notifyAll()、t.interrupt():t 退出 WaitSet,进入 EntryList 竞争锁。- 竞争锁成功:
WAITING→RUNNABLE - 竞争锁失败:
WAITING→BLOCKED
- 竞争锁成功:
park()
- 线程 t 调用
LockSupport.park():t 状态RUNNABLE→WAITING - 唤醒:其它线程调用
LockSupport.unpark()、t.interrupt():t 状态WAITING→RUNNABLE
3.2.2、TIMED_WAITING
不会被 CPU 分配时间片,等待显式唤醒。
相比 WAITING,等待超时后自动唤醒。
- sleep(long)
- 对应 join()、wait()、park() 的超时等待方法。
sleep(long)
- 线程 t 调用
Thread.sleep(long),状态RUNNABLE→TIMED_WAITING - 唤醒:
t1.interrupt()显式唤醒、等待超时自动唤醒,状态TIMED_WAITING→RUNNABLE
join(long)
- 线程 t1 调用
t1.join(long):t1 进入 t2 对象的 Monitor 的 WaitSet,t1 状态RUNNABLE→TIMED_WAITING
注意是当前线程在t 线程对象的监视器上等待 - 唤醒:状态
TIMED_WAITING→RUNNABLE- 显式唤醒:其他线程调用
t1.interrupt()、t2 运行结束 - 自动唤醒:t1 等待超时
- 显式唤醒:其他线程调用
wait(long)
线程 t 执行到 synchronized(obj){}获取对象锁,成为 Monitor 的 Owner 后:
- 线程 t 调用
obj.wait(long):t 进入 WaitSet,状态RUNNABLE→TIMED_WAITING - 唤醒:t 退出 WaitSet,进入 EntryList 竞争锁。
- 类型
- 显式唤醒:其它线程调用
obj.notify()、obj.notifyAll()、t.interrupt() - 自动唤醒:线程 t 等待超时。
- 显式唤醒:其它线程调用
- 竞争锁
- 竞争锁成功:
TIMED_WAITING→RUNNABLE - 竞争锁失败:
TIMED_WAITING→BLOCKED
- 竞争锁成功:
- 类型
parkXxx(long)
- 线程 t 调用
LockSupport.parkNanos(long)、LockSupport.parkUntil(long):t 状态RUNNABLE→TIMED_WAITING - 唤醒:t 状态
TIMED_WAITING→RUNNABLE- 显式唤醒:其它线程调用
LockSupport.unpark()、t.interrupt() - 自动唤醒:线程 t 等待超时。
- 显式唤醒:其它线程调用
3.2.3、BLOCKED
线程 t 竞争锁失败,状态 RUNNABLE → BLOCKED
- t 进入
synchronized(obj){...}竞争锁失败:进入 EntryList,状态RUNNABLE→BLOCKED - Monitor 的 Owner 执行结束,唤醒 EntryList 中所有阻塞线程进行锁竞争。
- 竞争成功:
BLOCKED→RUNNABLE - 竞争失败:
BLOCKED→RUNNABLE→BLOCKED
- 竞争成功:
3.3、RUNNABLE → TERMINATED
线程 t 代码执行结束:RUNNABLE → TERMINATED

浙公网安备 33010602011771号