一、先明确:Java的6种线程状态(Thread.State)
这是状态转换的"基本单元",所有转换都围绕这6种状态进行:
scss
1.NEW(新建):线程对象已创建(如new Thread()),但未调用start(),JVM未为其分配操作系统级线程资源。
2.RUNNABLE(可运行):调用start()后进入此状态,对应操作系统线程的"就绪"和"运行中"------线程要么在等待CPU调度(就绪),要么正在CPU上执行run()方法(运行中),JVM层面不区分这两个子状态。
3.BLOCKED(阻塞):仅因竞争synchronized锁失败而暂停,等待锁释放(不涉及Lock接口的锁,Lock锁竞争会进入WAITING/TIMED_WAITING)。
4.WAITING(无限等待):线程主动调用无参等待方法,释放CPU和持有的锁,必须依赖其他线程显式唤醒(否则永久等待)。
5.TIMED_WAITING(计时等待):线程调用带超时参数的等待方法,释放资源后仅等待指定时间,超时后自动唤醒,也可被提前唤醒。
6.TERMINATED(终止):线程的run()方法执行完毕,或因未捕获异常崩溃,生命周期彻底结束。

线程状态流程图
二、完整状态转换流程(带触发条件+场景)
线程从创建到终止,会经历以下核心转换路径,不同路径对应不同业务场景:
1. 初始启动:NEW → RUNNABLE
-
触发操作:调用线程对象的start()方法(注意:不能重复调用,否则抛IllegalThreadStateException)。
-
底层逻辑:start()会向JVM注册线程,JVM向操作系统申请创建线程(如Linux的pthread_create),操作系统将线程加入"就绪队列",等待CPU调度。
-
场景:
scssThread t = new Thread(() -> { /* 任务逻辑 */ }); System.out.println(t.getState()); // 输出 NEW t.start(); System.out.println(t.getState()); // 输出 RUNNABLE(大概率,因CPU调度有延迟)
2. 可运行态内部切换:RUNNABLE(就绪)↔ RUNNABLE(运行中)
-
触发操作:由操作系统的CPU调度算法控制,JVM不干预。
scss就绪→运行中:CPU空闲时,调度器从就绪队列选一个线程分配时间片,线程开始执行run()方法。 运行中→就绪:线程的CPU时间片用完,或有更高优先级线程进入就绪队列,当前线程被抢占,回到就绪队列。
-
特点:此过程是"隐式转换",无需代码触发,开发者无法通过Thread.State感知(始终显示为RUNNABLE)。
3. 锁竞争:RUNNABLE ↔ BLOCKED
仅针对synchronized锁的竞争,是"被动阻塞"(线程未主动放弃,因锁被占用而暂停)。
RUNNABLE → BLOCKED:
arduino
触发:线程尝试进入synchronized代码块/方法,但锁已被其他线程持有。
逻辑:线程从CPU调度队列退出,进入该锁的"阻塞队列",等待锁释放。
BLOCKED → RUNNABLE:
arduino
触发:持有synchronized锁的线程退出同步块,释放锁。
逻辑:JVM从该锁的阻塞队列中唤醒一个线程(公平/非公平取决于JVM实现),线程重新进入就绪队列,等待CPU调度。
场景:
scss
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) { /* 持有锁执行10秒 */ }
});
Thread t2 = new Thread(() -> {
System.out.println(t2.getState()); // 先 RUNNABLE
synchronized (lock) { /* 竞争锁失败 */ }
System.out.println(t2.getState()); // 竞争时变为 BLOCKED
});
t1.start();
Thread.sleep(100); // 确保t1先持有锁
t2.start();
4. 主动等待(无限):RUNNABLE ↔ WAITING
线程主动调用无参等待方法,释放CPU和锁,必须由其他线程显式唤醒(否则"卡死")。
RUNNABLE → WAITING(3种核心触发方式):
scss
1.线程持有synchronized锁时,调用lock.wait()(必须在同步块内,否则抛IllegalMonitorStateException)。
2.调用另一个线程的thread.join()(无参):等待目标线程执行完毕,若目标线程未结束,当前线程进入等待。
3.调用LockSupport.park()(无参):无需持有锁,直接暂停,需通过LockSupport.unpark(thread)唤醒。
WAITING → RUNNABLE(对应唤醒方式):
scss
1.其他线程调用lock.notify()/notifyAll()(唤醒后需重新竞争锁,竞争成功才回到RUNNABLE)。
2.join()的目标线程执行完毕(自动唤醒)。
3.其他线程调用LockSupport.unpark(thread)(直接唤醒,无需竞争锁)。
场景(wait/notify):
scss
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println(t1.getState()); // RUNNABLE
lock.wait(); // 释放锁,进入 WAITING
System.out.println(t1.getState()); // 被唤醒并重新获锁后,回到 RUNNABLE
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
lock.notify(); // 唤醒t1
}
});
t1.start();
Thread.sleep(100);
System.out.println(t1.getState()); // 输出 WAITING
t2.start();
5. 主动等待(计时):RUNNABLE ↔ TIMED_WAITING
线程调用带超时参数的等待方法,释放资源后等待指定时间,超时后自动唤醒(也可被提前唤醒)。
RUNNABLE → TIMED_WAITING(5种核心触发方式):
arduino
1.Thread.sleep(long ms):不释放锁,仅暂停指定时间(最常用,无需持有锁)。
2.持有synchronized锁时,调用lock.wait(long ms)。
3.调用thread.join(long ms):等待目标线程指定时间,超时后不再等。
4.LockSupport.parkNanos(long nanos)/parkUntil(long deadline):带超时的暂停。
5.线程池中的线程等待任务(如ThreadPoolExecutor的awaitTermination(long, TimeUnit))。
TIMED_WAITING → RUNNABLE(2种唤醒方式):
scss
1.等待时间到期(自动唤醒)。
2.被其他线程显式唤醒(如notify()/unpark(),与WAITING的唤醒逻辑一致)。
场景(sleep):
scss
Thread t = new Thread(() -> {
System.out.println(t.getState()); // RUNNABLE
try {
Thread.sleep(1000); // 进入 TIMED_WAITING
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(t.getState()); // 超时后回到 RUNNABLE
});
t.start();
Thread.sleep(100);
System.out.println(t.getState()); // 输出 TIMED_WAITING
6. 最终终止:RUNNABLE → TERMINATED
线程生命周期的终点,一旦进入此状态,无法再回到其他状态。
触发条件:
scss
1.线程的run()方法正常执行完毕(无异常)。
2.线程在run()方法中抛出未捕获的异常(如NullPointerException),导致线程崩溃。
3.其他线程调用thread.stop()(已废弃,会强制终止线程,可能导致资源泄漏)。
场景:
scss
Thread t = new Thread(() -> {
// 任务执行1秒后结束
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
t.start();
Thread.sleep(2000); // 等待t执行完毕
System.out.println(t.getState()); // 输出 TERMINATED
三、关键注意点
1.BLOCKED vs WAITING/TIMED_WAITING:
scss
BLOCKED是"被动等锁"(仅因synchronized锁竞争),不释放已持有的锁;
WAITING/TIMED_WAITING是"主动等待",会释放已持有的锁(sleep()除外,不释放锁)。
2.唤醒后的锁竞争:
scss
由wait()唤醒的线程,必须重新竞争synchronized锁,竞争成功才会从WAITING/TIMED_WAITING进入RUNNABLE,否则会进入BLOCKED状态。
3.中断(interrupt())的影响:
arduino
若线程处于WAITING/TIMED_WAITING状态时被中断(其他线程调用thread.interrupt()),会抛出InterruptedException,并清除中断标志,线程从等待状态回到RUNNABLE(需处理异常)。
BLOCKED状态的线程被中断,不会抛出异常,仅设置中断标志,状态仍为BLOCKED。
通过以上流程,可清晰理解线程在不同场景下的状态变化,以及代码操作对状态的影响。