Java线程状态全解析
目录
3. BLOCKED(阻塞状态):等待监视器锁(synchronized)
5. TIMED_WAITING(超时等待状态):指定时间内等待
6. TERMINATED(终止状态):线程已执行完毕或异常终止
在Java并发编程中,线程是核心执行单元,其状态的变化直接决定了程序的运行效率和正确性。很多开发者在学习线程时,容易混淆线程的不同状态,以及状态之间的切换条件——比如“NEW状态”和“RUNNABLE状态”有何关联?“BLOCKED状态”“WAITING状态”和“TIMED_WAITING状态”到底有何区别?今天这篇博客,就结合Java官方标准(java.lang.Thread.State枚举类),系统总结Java线程的6种标准状态,结合原理和实例,把每个状态的含义、触发条件以及切换逻辑讲清楚,帮你彻底理清线程状态的来龙去脉。
一、Java线程的6种标准状态
首先要明确核心前提:Java线程的状态由JVM严格定义,封装在java.lang.Thread.State枚举类中,共6种标准状态,并非操作系统层面的线程状态(操作系统线程通常有就绪、运行、阻塞等状态,与Java线程状态有对应关系,但不完全一致)。
Java线程的6种标准状态及核心描述如下,这6种状态覆盖了线程从创建到销毁的全生命周期:
| 状态 | 描述 |
|---|---|
| NEW | 线程已创建但尚未启动 |
| RUNNABLE | 线程正在JVM中执行,等待操作系统调度(包含就绪和运行两种逻辑状态) |
| BLOCKED | 线程被阻塞,等待监视器锁(synchronized锁) |
| WAITING | 线程无限期等待另一个线程执行特定操作 |
| TIMED_WAITING | 线程在指定时间内等待另一个线程执行操作或超时自动唤醒 |
| TERMINATED | 线程已执行完毕或异常终止 |
这里需要特别说明:此前部分资料会将“就绪”和“运行”拆分为两个逻辑状态,但根据Java官方定义,这两种情况统一归为RUNNABLE状态——因为从JVM层面来看,无论是等待CPU调度(就绪),还是正在执行(运行),线程都处于“可运行”的范畴,唯一的区别是是否获得CPU执行权。而BLOCKED、WAITING、TIMED_WAITING均属于“非运行状态”,即线程暂时无法获取CPU执行权,需等待特定条件满足后才能恢复。
二、每种线程状态的具体含义(结合官方定义+实例)
下面逐一解析6种标准状态的详细含义、典型触发场景,结合代码示例帮助大家快速联想,避免死记硬背,同时贴合官方枚举类的定义规范。
1. NEW(新建状态):线程已创建但尚未启动
当我们通过new Thread()创建一个线程对象时,线程就进入了NEW状态。此时,JVM已经为线程分配了内存(存储线程对象的信息,如线程名称、优先级等),但尚未调用线程的start()方法,线程的执行逻辑(run()方法)完全没有开始。
核心特征:线程对象已存在,但未与操作系统的底层线程关联,没有真正启动执行。
典型场景:
// 线程进入NEW状态(仅创建对象,未启动)
Thread thread = new Thread(() -> {
System.out.println("线程执行逻辑");
});
// 此时thread.getState()返回Thread.State.NEW
注意:此时线程还没有与操作系统的线程关联,只有调用start()方法后,JVM才会请求操作系统创建一个新的底层线程,线程才会脱离NEW状态。
2. RUNNABLE(可运行状态):等待调度或正在执行
当线程调用start()方法后,线程就进入了RUNNABLE状态。这是Java官方定义的核心状态,包含两种逻辑场景,均对应操作系统的“就绪状态”:
就绪场景:线程已与操作系统底层线程关联,JVM将其放入“就绪队列”,等待CPU调度,具备所有运行所需条件,仅缺少CPU执行权;
运行场景:线程获得CPU时间片,正在执行
run()方法中的逻辑。
从JVM视角来看,这两种场景无需区分,统一归为RUNNABLE状态——因为线程是否能执行,取决于CPU调度,而非JVM控制。
典型场景:
Thread thread = new Thread(() -> {
System.out.println("线程执行逻辑");
});
thread.start(); // 调用start()后,线程进入RUNNABLE状态
// 此时thread.getState()返回Thread.State.RUNNABLE(无论是否获得CPU)
补充:RUNNABLE状态是一个“动态过渡状态”,线程不会一直停留在这个状态——要么获得CPU时间片执行逻辑,要么因某些原因(如获取锁失败、调用等待方法)脱离该状态,进入BLOCKED、WAITING或TIMED_WAITING状态。
3. BLOCKED(阻塞状态):等待监视器锁(synchronized)
根据官方定义,BLOCKED状态的唯一触发原因是:线程试图获取一个被其他线程持有的监视器锁(即synchronized锁,包括同步方法、同步代码块的锁),此时线程会被阻塞,直到成功获取到锁,才能恢复到RUNNABLE状态。
核心特征:仅因“获取synchronized锁失败”而阻塞,其他阻塞场景(如等待通知、超时等待)不属于该状态。
典型场景:
Object lock = new Object();
// 线程1先获取锁,进入RUNNABLE状态并执行
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(1000); // 后续会进入TIMED_WAITING状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 线程2试图获取锁,但锁被线程1持有,进入BLOCKED状态
Thread thread2 = new Thread(() -> {
synchronized (lock) { // 此处阻塞,进入BLOCKED状态
System.out.println("线程2执行");
}
});
thread1.start();
thread2.start();
// 线程2启动后,getState()返回Thread.State.BLOCKED
4. WAITING(无限期等待状态):无限期等待特定操作
WAITING状态是“无限期等待”,即线程会一直等待,直到另一个线程执行特定的唤醒操作(如Object.notify()、Object.notifyAll()),否则会一直处于等待状态,无法自动恢复。
核心触发条件:线程主动调用以下方法,会进入
WAITING状态(调用后会释放持有的监视器锁):
Object.wait():无超时参数,需其他线程调用同一对象的notify()或notifyAll()唤醒;
Thread.join():无超时参数,等待目标线程执行完毕后自动唤醒;
LockSupport.park():无超时参数,需其他线程调用LockSupport.unpark(Thread)唤醒。
典型场景:
Object lock = new Object();
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(); // 调用无参wait(),进入WAITING状态,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1被唤醒");
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.notify(); // 唤醒thread1,使其从WAITING恢复为RUNNABLE
}
});
thread1.start();
thread2.start();
5. TIMED_WAITING(超时等待状态):指定时间内等待
TIMED_WAITING状态是“有限期等待”,与WAITING状态的核心区别是:线程等待一段时间后,若未被其他线程唤醒,会自动唤醒,恢复到RUNNABLE状态;若在超时前被唤醒,也会提前恢复。
核心触发条件:线程调用带有超时参数的等待方法,会进入
TIMED_WAITING状态(部分方法会释放监视器锁):
Thread.sleep(long millis):睡眠指定时间,不释放锁,超时后自动唤醒;
Object.wait(long timeout):等待指定时间,释放锁,超时或被唤醒后恢复;
Thread.join(long millis):等待目标线程指定时间,超时或目标线程执行完毕后恢复;
LockSupport.parkNanos(long nanos)、LockSupport.parkUntil(long deadline):超时等待,需唤醒或超时后恢复。
典型场景:
// 场景1:Thread.sleep()触发TIMED_WAITING(不释放锁)
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000); // 睡眠1秒,进入TIMED_WAITING状态
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1超时自动唤醒");
});
// 场景2:Object.wait(long)触发TIMED_WAITING(释放锁)
Object lock = new Object();
Thread thread2 = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(1000); // 等待1秒,超时自动唤醒或被notify()唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
6. TERMINATED(终止状态):线程已执行完毕或异常终止
TERMINATED是线程的最终状态,一旦进入该状态,线程就会被JVM回收,无法再切换到其他任何状态,也无法通过start()方法重新启动。
进入终止状态的两种情况:
正常终止:线程的
run()方法执行完毕,逻辑正常结束,线程自然终止;异常终止:线程在运行过程中抛出未捕获的异常(如NullPointerException、InterruptedException),导致线程被迫终止。
典型场景:
// 正常终止:run()方法执行完毕
Thread thread1 = new Thread(() -> {
System.out.println("线程1执行完毕"); // run()执行完,进入TERMINATED状态
});
// 异常终止:抛出未捕获异常
Thread thread2 = new Thread(() -> {
int i = 1 / 0; // 抛出ArithmeticException,线程被迫终止
});
thread1.start();
thread2.start();
// 线程执行完毕后,getState()返回Thread.State.TERMINATED
注意:调用线程的stop()方法也能强制终止线程,但该方法已被废弃(会导致线程资源无法正常释放,引发数据不一致问题),不推荐使用。
三、线程状态之间的切换条件
线程的6种状态之间并非孤立存在,而是会根据不同的操作和场景相互切换。
1. 核心状态切换流程
NEW → RUNNABLE → (BLOCKED / WAITING / TIMED_WAITING) → RUNNABLE → TERMINATED
补充说明:
RUNNABLE状态可直接切换到BLOCKED、WAITING、TIMED_WAITING状态,也可直接切换到TERMINATED状态;
BLOCKED、WAITING、TIMED_WAITING状态,无法直接切换到TERMINATED状态,必须先恢复为RUNNABLE状态,再由RUNNABLE状态切换到TERMINATED;
NEW状态只能切换到RUNNABLE状态,无法直接切换到其他状态;TERMINATED状态无法切换到任何其他状态。
2. 具体切换条件详解(结合官方定义)
NEW → RUNNABLE:
触发条件:调用线程的
start()方法。说明:new Thread()只是创建线程对象(NEW状态),调用start()后,JVM请求操作系统创建底层线程,线程进入可运行状态,等待CPU调度。
RUNNABLE → BLOCKED:
触发条件:线程试图获取synchronized监视器锁,且该锁被其他线程持有。
说明:获取锁失败后,线程从RUNNABLE状态进入BLOCKED状态;当持有锁的线程释放锁,且当前线程成功获取锁,会从BLOCKED状态恢复为RUNNABLE状态。
RUNNABLE → WAITING:
触发条件:线程调用无超时参数的等待方法(
Object.wait()、Thread.join()、LockSupport.park())。说明:调用这些方法后,线程主动放弃CPU执行权,进入无限期等待;被其他线程唤醒后,恢复为RUNNABLE状态。
RUNNABLE → TIMED_WAITING:
触发条件:线程调用带有超时参数的等待方法(
Thread.sleep(long)、Object.wait(long)、Thread.join(long)等)。说明:线程进入有限期等待,要么超时后自动唤醒,要么被其他线程提前唤醒,两种情况都会恢复为RUNNABLE状态。
BLOCKED → RUNNABLE:
触发条件:线程成功获取到之前未获取到的synchronized监视器锁。
WAITING → RUNNABLE:
触发条件:其他线程执行唤醒操作(如
Object.notify()、LockSupport.unpark()),或目标线程执行完毕(Thread.join()场景)。
TIMED_WAITING → RUNNABLE:
触发条件:等待时间超时,或其他线程执行唤醒操作。
RUNNABLE → TERMINATED:
触发条件:
① run()方法执行完毕,正常终止;
② 线程运行过程中抛出未捕获的异常,异常终止;
③ (不推荐)调用
Thread.stop()方法,强制终止。
NEW → TERMINATED:
特殊情况:线程创建后(NEW状态),未调用start()方法,线程对象被垃圾回收,这种情况极少出现,实际开发中可忽略。
四、常见误区
学习线程状态时,很多开发者会有一些常见的误解,这里结合官方定义逐一澄清,帮你避开坑点:
💡 误区1:调用start()方法会立即执行线程逻辑? 纠正:不会。调用start()后,线程进入RUNNABLE状态,等待CPU调度,只有获得时间片后才会执行run()方法,并非立即执行。
💡 误区2:Thread.sleep()会释放synchronized锁? 纠正:不会。sleep()方法只会让线程进入TIMED_WAITING状态,放弃CPU执行权,但不会释放线程持有的synchronized锁;而Object.wait()方法会释放锁。
💡 误区3:BLOCKED状态和WAITING状态没有区别? 纠正:区别极大。BLOCKED仅因“获取synchronized锁失败”触发,而WAITING是线程主动调用无超时等待方法触发;且BLOCKED状态会在锁释放后自动竞争锁,WAITING状态必须依赖其他线程唤醒。
💡 误区4:线程终止后,还能调用start()方法重新启动? 纠正:不能。线程一旦进入TERMINATED状态,就会被JVM回收,无法再调用start()方法启动,否则会抛出IllegalThreadStateException异常。
💡 误区5:RUNNABLE状态就是线程正在执行? 纠正:不是。RUNNABLE状态包含“就绪”和“运行”两种逻辑场景,只有当线程获得CPU时间片时,才会执行run()方法;未获得时间片时,只是等待调度。
五、总结
Java线程的6种标准状态(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED),是Java并发编程的基础,其定义严格遵循JVM规范,覆盖了线程从创建到销毁的全生命周期。
理解线程状态的关键,不仅要记住每种状态的官方描述,更要掌握状态之间的切换条件——尤其是区分BLOCKED、WAITING、TIMED_WAITING三种非运行状态的触发场景,这是面试中高频考察的重点,也是后续学习线程同步、线程池、锁机制的基础。
最后,建议大家结合代码实例,亲自调试线程的状态(通过Thread.getState()方法获取当前线程状态),观察状态切换的过程,通过实践加深理解,避免死记硬背。只有真正掌握线程状态,才能在并发编程中规避线程安全问题,提升程序的运行效率。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)