Java 线程状态转换(WAITING -> RUNNABLE 触发条件)
下文笔者讲述java中线程状态转换相关说明,如下所示
场景2:
场景4:
WAITING 与 RUNNABLE 状态简介
1. WAITING(无时限等待):
线程处于主动等待状态,无超时时间限制,
若没有外部触发条件,会永久等待,无法自行唤醒,
此时线程不会被 CPU 调度,释放CPU执行权和锁资源
2. RUNNABLE(可运行/运行):
线程处于就绪状态(等待CPU调度)
或
运行状态(正在执行任务)
是线程执行业务逻辑的核心状态,由 JVM 线程调度器负责调度。
WAITING -> RUNNABLE 的转换,
本质是外部触发线程唤醒条件,
使线程结束等待,
进入就绪队列等待CPU调度
线程状态转换的核心触发条件
Java中线程进入WAITING状态的场景固定,
对应的唤醒条件(即 WAITING -> RUNNABLE 转换条件)也一一对应,
核心分为 5 大类场景:
场景1:
Object.wait() 等待 → Object.notify()/notifyAll() 唤醒
这是最基础的等待-唤醒机制,基于对象监视器(synchronized 锁)实现,
线程通过 `Object.wait()` 进入WAITING 状态,
需通过其他线程调用同一对象的 `notify()` 或 `notifyAll()` 唤醒。
核心触发条件:
1.等待线程:
必须在 synchronized 同步代码块/方法中
调用 `Object.wait()`(释放对象锁,进入该对象的等待队列,变为 WAITING 状态)
2.唤醒线程:
必须在 同一对象的 synchronized 同步代码块/方法中 调用 `object.notify()`
(唤醒等待队列中的任意一个线程)
或 `object.notifyAll()`(唤醒等待队列中的所有线程);
3.唤醒后:被唤醒的线程不会立即变为 RUNNABLE,
而是先重新竞争该对象的 synchronized 锁,
竞争成功后才会从 WAITING 转为 RUNNABLE
示例
public class WaitNotifyDemo {
// 共享对象(作为锁对象和等待队列载体)
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
// 等待线程
Thread waitThread = new Thread(() -> {
synchronized (LOCK) {
try {
System.out.println(Thread.currentThread().getName() + ":进入WAITING状态,释放锁");
LOCK.wait(); // 进入WAITING状态,释放LOCK锁
System.out.println(Thread.currentThread().getName() + ":被唤醒,转为RUNNABLE状态");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "等待线程");
// 唤醒线程
Thread notifyThread = new Thread(() -> {
synchronized (LOCK) {
System.out.println(Thread.currentThread().getName() + ":获取锁,准备唤醒等待线程");
LOCK.notify(); // 唤醒LOCK对象等待队列中的一个线程
// LOCK.notifyAll(); // 唤醒所有等待线程
System.out.println(Thread.currentThread().getName() + ":唤醒操作完成,即将释放锁");
}
}, "唤醒线程");
waitThread.start();
Thread.sleep(1000); // 确保等待线程先进入WAITING状态
notifyThread.start();
waitThread.join();
notifyThread.join();
System.out.println("执行完成");
}
}
运行结果:
等待线程:
进入WAITING状态,释放锁
唤醒线程:
获取锁,准备唤醒等待线程
唤醒线程:
唤醒操作完成,即将释放锁
等待线程:
被唤醒,转为RUNNABLE状态
执行完成
场景2:
Thread.join() 等待 → 目标线程执行完毕唤醒
当线程A调用线程 B 的 `join()` 方法时
线程 A 会进入 WAITING 状态,
等待线程 B 执行完毕,
线程 B 终止后,
线程 A 会自动从 WAITING 转为 RUNNABLE
核心触发条件:
1.等待线程(A):调用目标线程(B
`Thread.join()`(无参方法,无超时等待),进入 WAITING 状态;
2.唤醒触发:当目标线程(B)执行完毕(正常终止或异常终止),
JVM 会自动唤醒线程 A,使其从 WAITING 转为 RUNNABLE;
3.补充:`join(long millis)` 是 TIMED_WAITING 状态,
并非 WAITING,超时后会自动唤醒。
例
public class ThreadJoinDemo {
public static void main(String[] args) throws InterruptedException {
// 目标线程(被join的线程)
Thread targetThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":开始执行,耗时3秒");
try {
Thread.sleep(3000); // 模拟业务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":执行完毕,即将终止");
}, "目标线程");
// 等待线程(主线程中创建)
Thread waitThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":等待目标线程执行完毕");
try {
targetThread.join(); // 调用join(),进入WAITING状态
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":目标线程已终止,转为RUNNABLE状态");
}, "等待线程");
targetThread.start();
waitThread.start();
waitThread.join();
System.out.println("主线程执行完成");
}
}
运行结果:
目标线程:开始执行,耗时3秒
等待线程:等待目标线程执行完毕
目标线程:执行完毕,即将终止
等待线程:目标线程已终止,转为RUNNABLE状态
主线程执行完成
场景3:LockSupport.park() 等待 → LockSupport.unpark(Thread) 唤醒
`LockSupport` 是 JDK 提供的底层线程阻塞/唤醒工具,
无需依赖 synchronized 锁或 Lock 锁,线程通过 `LockSupport.park()` 进入 WAITING 状态,
需通过其他线程调用 `LockSupport.unpark(目标线程)` 唤醒。
核心触发条件:
1.等待线程:调用 `LockSupport.park()`(无参方法,无超时等待),
进入 WAITING 状态(称为“停车”),无需持有任何锁;
2.唤醒线程:调用 `LockSupport.unpark(Thread target)`,
指定唤醒目标线程(称为“解锁停车”),无需在同步块中执行;
3.特性:`unpark()` 可在 `park()` 之前调用(提前“许可”),
线程后续调用 `park()` 时会直接跳过等待,不会进入 WAITING 状态;
唤醒后线程直接转为 RUNNABLE,无需竞争锁。
例
import java.util.concurrent.locks.LockSupport;
public class LockSupportParkUnparkDemo {
public static void main(String[] args) throws InterruptedException {
// 等待线程
Thread parkThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":准备进入WAITING状态");
LockSupport.park(); // 停车,进入WAITING状态
System.out.println(Thread.currentThread().getName() + ":被唤醒,转为RUNNABLE状态");
}, "等待线程(Park)");
parkThread.start();
Thread.sleep(2000); // 确保等待线程进入WAITING状态
// 唤醒线程
Thread unparkThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":准备唤醒等待线程");
LockSupport.unpark(parkThread); // 解锁停车,唤醒目标线程
System.out.println(Thread.currentThread().getName() + ":唤醒操作完成");
}, "唤醒线程(Unpark)");
unparkThread.start();
parkThread.join();
unparkThread.join();
System.out.println("执行完成");
}
}
运行结果
等待线程(Park):准备进入WAITING状态
唤醒线程(Unpark):准备唤醒等待线程
唤醒线程(Unpark):唤醒操作完成
等待线程(Park):被唤醒,转为RUNNABLE状态
执行完成
场景4:
Condition.await() 等待 → Condition.signal()/signalAll() 唤醒
`Condition` 是基于 `Lock` 锁的条件等待机制,
替代了 `Object.wait()`/`notify()`,功能更灵活,
线程通过 `Condition.await()` 进入 WAITING 状态,
需通过其他线程调用同一 `Condition` 的 `signal()`/`signalAll()` 唤醒。
核心触发条件:
1. 等待线程:必须在 Lock 锁获取之后(`lock.lock()` 之后)
调用 `condition.await()`(释放 Lock 锁,进入 Condition 的等待队列,变为 WAITING 状态);
2.唤醒线程:必须在 同一 Lock 锁获取之后 调用 `condition.signal()`
(唤醒 Condition 等待队列中的一个线程)或 `condition.signalAll()`(唤醒所有等待线程);
3.唤醒后:被唤醒的线程需重新竞争 Lock 锁,
竞争成功后才会从 WAITING 转为 RUNNABLE,继续执行后续逻辑。
例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionAwaitSignalDemo {
// 可重入锁
private static final Lock LOCK = new ReentrantLock();
// 基于Lock创建Condition条件对象
private static final Condition CONDITION = LOCK.newCondition();
public static void main(String[] args) throws InterruptedException {
// 等待线程
Thread awaitThread = new Thread(() -> {
LOCK.lock(); // 获取Lock锁
try {
System.out.println(Thread.currentThread().getName() + ":进入WAITING状态,释放Lock锁");
CONDITION.await(); // 进入Condition等待队列,变为WAITING状态
System.out.println(Thread.currentThread().getName() + ":被唤醒,转为RUNNABLE状态");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
LOCK.unlock(); // 释放Lock锁
System.out.println(Thread.currentThread().getName() + ":释放Lock锁");
}
}, "等待线程(Await)");
// 唤醒线程
Thread signalThread = new Thread(() -> {
LOCK.lock(); // 获取Lock锁
try {
System.out.println(Thread.currentThread().getName() + ":获取Lock锁,准备唤醒等待线程");
CONDITION.signal(); // 唤醒Condition等待队列中的一个线程
// CONDITION.signalAll(); // 唤醒所有等待线程
System.out.println(Thread.currentThread().getName() + ":唤醒操作完成");
} finally {
LOCK.unlock(); // 释放Lock锁
System.out.println(Thread.currentThread().getName() + ":释放Lock锁");
}
}, "唤醒线程(Signal)");
awaitThread.start();
Thread.sleep(1000); // 确保等待线程先进入WAITING状态
signalThread.start();
awaitThread.join();
signalThread.join();
System.out.println("执行完成");
}
}
运行结果
等待线程(Await):进入WAITING状态,释放Lock锁
唤醒线程(Signal):获取Lock锁,准备唤醒等待线程
唤醒线程(Signal):唤醒操作完成
唤醒线程(Signal):释放Lock锁
等待线程(Await):被唤醒,转为RUNNABLE状态
等待线程(Await):释放Lock锁
执行完成
场景5:线程中断(InterruptedException)唤醒
无论线程是通过 `Object.wait()`、`Condition.await()` 还是 `Thread.join()` 进入 WAITING 状态,
若其他线程调用该等待线程的 `interrupt()` 方法,会触发 `InterruptedException` 异常,
线程会从 WAITING 状态被唤醒,转为 RUNNABLE 状态(实际是先进入异常处理流程,最终转为 RUNNABLE)。
核心触发条件:
1.等待线程:处于 WAITING 状态(由 `wait()`/`await()`/`join()` 触发);
2.中断线程:调用等待线程的 `Thread.interrupt()` 方法,设置中断标志位;
3.唤醒结果:等待线程抛出 `InterruptedException` 异常,中断标志位被清除,
线程从 WAITING 转为 RUNNABLE,进入异常处理逻辑。
例:
public class InterruptWaitingThreadDemo {
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
// 等待线程(通过wait()进入WAITING状态)
Thread waitingThread = new Thread(() -> {
synchronized (LOCK) {
try {
System.out.println(Thread.currentThread().getName() + ":进入WAITING状态");
LOCK.wait(); // 无时限等待
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + ":收到中断信号,被唤醒(抛出InterruptedException)");
System.out.println(Thread.currentThread().getName() + ":当前线程中断标志位:" + Thread.currentThread().isInterrupted()); // false,标志位已清除
}
System.out.println(Thread.currentThread().getName() + ":转为RUNNABLE状态,继续执行");
}
}, "等待线程");
waitingThread.start();
Thread.sleep(2000); // 确保线程进入WAITING状态
// 中断线程:调用interrupt()唤醒等待线程
Thread interruptThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":准备中断等待线程");
waitingThread.interrupt();
System.out.println(Thread.currentThread().getName() + ":中断操作完成");
}, "中断线程");
interruptThread.start();
waitingThread.join();
interruptThread.join();
System.out.println("执行完成");
}
}
运行结果
等待线程:进入WAITING状态
中断线程:准备中断等待线程
中断线程:中断操作完成
等待线程:收到中断信号,被唤醒(抛出InterruptedException)
等待线程:当前线程中断标志位:false
等待线程:转为RUNNABLE状态,继续执行
执行完成
注意事项
1. WAITING 与 TIMED_WAITING 的区别:
TIMED_WAITING 是有时限等待(如 `wait(1000)`、`sleep(1000)`),
超时后会自动转为 RUNNABLE;而 WAITING 无时限,
必须依赖外部触发条件才能唤醒,二者唤醒逻辑不同。
2. 唤醒后并非立即执行:
基于`Object.wait()`/`Condition.await()` 的唤醒,线程需重新竞争锁资源,
只有竞争成功后才会真正转为 RUNNABLE 并执行;
基于`LockSupport.unpark()` 的唤醒,
线程会直接进入 RUNNABLE 队列,无需竞争锁。
3. `notify()` 与 `notifyAll()` 的选择:
`notify()` 仅唤醒一个等待线程,
可能存在唤醒“错误线程”的问题;
`notifyAll()` 唤醒所有等待线程,虽会增加锁竞争,
但能确保需要被唤醒的线程得到唤醒,
推荐在不确定唤醒目标时使用 `notifyAll()`。
4. `LockSupport` 的许可机制:
`LockSupport` 基于“许可”实现,
每个线程最多持有一个许可,`unpark()` 会发放许可,
`park()` 会消耗许可;若提前调用 `unpark()`,后续 `park()` 会直接返回,
不会进入 WAITING 状态。
5. 中断唤醒会清除标志位:
等待线程被中断唤醒后,`InterruptedException` 会清除中断标志位,
若后续需要感知中断状态,
需在异常块中调用 `Thread.currentThread().interrupt()` 重新设置标志位。
Java 线程 WAITING -> RUNNABLE 状态转换的核心触发条件可归纳为 5 类,核心要点如下: 1.基础唤醒:`Object.wait()` → `Object.notify()`/`notifyAll()`(依赖 synchronized 锁); 2.线程终止唤醒:`Thread.join()` → 目标线程执行完毕(自动唤醒); 3.底层工具唤醒:`LockSupport.park()` → `LockSupport.unpark(Thread)`(无需锁,支持提前唤醒); 4.条件唤醒:`Condition.await()` → `Condition.signal()`/`signalAll()`(依赖 Lock 锁,功能更灵活); 5.中断唤醒:任意 WAITING 线程 → 被调用 `interrupt()`(抛出 InterruptedException,清除中断标志位)。
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


