Java线程状态详解:从创建到终止的完整生命周期
本文深入解析Java线程的6种状态及其转换机制。线程状态包括NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(无限等待)、TIMED_WAITING(限期等待)和TERMINATED(终止)。文章详细阐述了每种状态的特点、触发条件和转换规则,并提供了示例代码说明。特别对比了sleep()与wait()方法的关键区别,以及synchronized锁对状态的影响。
Java线程状态详解:从创建到终止的完整生命周期
在Java并发编程中,理解线程的状态及其转换机制是掌握多线程编程的基础。线程作为程序执行的最小单元,其状态变化直接反映了程序的运行情况。本文将详细解析Java线程的6种状态,以及状态之间的转换规则,帮助你深入理解线程的生命周期。
一、Java线程状态的定义
Java中线程的状态被定义在java.lang.Thread.State
枚举类中,共包含6种状态:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED
}
这些状态是JVM层面的定义,与操作系统底层的线程状态(如就绪、运行、阻塞等)并不完全一一对应,但反映了线程在JVM中的生命周期阶段。我们可以通过Thread.getState()
方法获取当前线程的状态。
二、6种线程状态详解
1. NEW(新建状态)
定义:当线程对象被创建但尚未调用start()
方法时,线程处于NEW状态。
特点:
- 线程尚未启动,没有分配底层操作系统资源
- 此时线程仅作为一个Java对象存在于堆内存中
- 可以调用
start()
方法启动,或调用stop()
(已废弃)终止
示例:
Thread thread = new Thread(() -> {
// 线程执行逻辑
});
System.out.println(thread.getState()); // 输出:NEW
2. RUNNABLE(可运行状态)
定义:调用start()
方法后,线程进入RUNNABLE状态。该状态包含两种情况:
- 线程正在CPU上执行(运行中)
- 线程处于就绪状态,等待CPU调度(未运行但可立即执行)
特点:
- 线程已获取必要的资源(如栈空间),并被纳入线程调度器管理
- 处于此状态的线程可能正在执行,也可能在等待CPU时间片
- 这是Java对"就绪"和"运行"两种操作系统状态的统一抽象
触发条件:
- 新建线程调用
start()
方法后进入RUNNABLE状态 - 其他状态(如BLOCKED、WAITING等)的线程恢复后进入RUNNABLE状态
注意:start()
方法只能调用一次,多次调用会抛出IllegalThreadStateException
。
3. BLOCKED(阻塞状态)
定义:线程因等待获取监视器锁(synchronized锁)而处于阻塞状态。
特点:
- 仅与同步机制(synchronized关键字)相关,是一种"主动等待"锁的状态
- 当线程试图进入
synchronized
修饰的方法或代码块时,若锁已被其他线程持有,则当前线程进入BLOCKED状态 - 一旦持有锁的线程释放锁,JVM会从BLOCKED状态的线程中选择一个唤醒,使其进入RUNNABLE状态
示例场景:
// 共享锁对象
Object lock = new Object();
// 线程1先获取锁
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(1000); // 持有锁休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 线程2后获取锁,会进入BLOCKED状态
Thread thread2 = new Thread(() -> {
synchronized (lock) { // 此时锁被thread1持有
System.out.println("Thread2获得锁");
}
});
thread1.start();
Thread.sleep(100); // 确保thread1先启动
thread2.start();
Thread.sleep(100);
System.out.println(thread2.getState()); // 输出:BLOCKED
4. WAITING(无限期等待状态)
定义:线程进入一种无时间限制的等待状态,需要其他线程显式唤醒才能继续执行。
特点:
- 处于WAITING状态的线程不会被分配CPU时间片
- 必须由其他线程执行特定操作(如调用
notify()
)才能唤醒 - 此状态的线程已释放持有的监视器锁(若有)
进入WAITING状态的方法:
Object.wait()
:当前线程释放锁并进入WAITING状态,需被其他线程notify()
或notifyAll()
唤醒Thread.join()
:等待目标线程执行完毕,需目标线程终止才能唤醒LockSupport.park()
:无参数版本,需其他线程调用LockSupport.unpark(Thread)
唤醒
示例:
Object lock = new Object();
Thread thread = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(); // 释放锁并进入WAITING状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // 输出:WAITING
5. TIMED_WAITING(限期等待状态)
定义:线程进入一种有时间限制的等待状态,无需其他线程唤醒,等待时间结束后会自动恢复;也可被提前唤醒。
特点:
- 与WAITING状态类似,但有超时机制
- 超时时间到达后会自动转换为RUNNABLE状态
- 也可被其他线程提前唤醒(如
notify()
或中断)
进入TIMED_WAITING状态的方法:
Thread.sleep(long millis)
:线程休眠指定时间,不释放监视器锁Object.wait(long timeout)
:释放锁并等待指定时间,超时后自动唤醒Thread.join(long millis)
:等待目标线程指定时间LockSupport.parkNanos(long nanos)
:限时暂停当前线程LockSupport.parkUntil(long deadline)
:暂停至指定时间点
示例:
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000); // 进入TIMED_WAITING状态
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // 输出:TIMED_WAITING
注意:Thread.sleep()
与Object.wait()
的核心区别:
sleep()
是Thread
类的静态方法,wait()
是Object
类的实例方法sleep()
不释放监视器锁,wait()
会释放监视器锁sleep()
时间到后自动唤醒,wait()
需显式唤醒或超时唤醒
6. TERMINATED(终止状态)
定义:线程执行完毕(run()
方法正常返回)或因异常终止后,进入TERMINATED状态。
特点:
- 线程的生命周期已结束,无法再回到其他状态
- 线程对象仍然存在于堆中,但已不具备执行能力
- 可以调用
isAlive()
方法判断线程是否已终止(返回false
)
示例:
Thread thread = new Thread(() -> {
// 空实现,立即执行完毕
});
thread.start();
Thread.sleep(100); // 等待线程执行完毕
System.out.println(thread.getState()); // 输出:TERMINATED
System.out.println(thread.isAlive()); // 输出:false
三、线程状态转换全解析
线程状态之间的转换遵循特定规则,下图展示了完整的状态转换路径:
NEW → RUNNABLE → TERMINATED
↑ ↓
├→ BLOCKED → RUNNABLE
│
├→ WAITING → RUNNABLE
│ ↑ ↓
│ └──────┘
│
└→ TIMED_WAITING → RUNNABLE
↑ ↓
└────────────┘
详细转换规则:
-
NEW → RUNNABLE
唯一途径:调用start()
方法。JVM会为线程分配底层资源,并将其加入调度队列。 -
RUNNABLE → BLOCKED
当线程试图获取synchronized
锁但该锁被其他线程持有时,进入BLOCKED状态。 -
BLOCKED → RUNNABLE
当持有synchronized
锁的线程释放锁,且当前线程获得该锁时,从BLOCKED转换为RUNNABLE。 -
RUNNABLE → WAITING
线程执行以下操作时进入WAITING状态:- 持有锁时调用
Object.wait()
- 调用其他线程的
join()
(无参) - 调用
LockSupport.park()
- 持有锁时调用
-
WAITING → RUNNABLE
唤醒条件:- 其他线程调用
Object.notify()
或notifyAll()
,且当前线程重新获得锁 - 被
join()
的线程执行完毕 - 其他线程调用
LockSupport.unpark(Thread)
- 其他线程调用
-
RUNNABLE → TIMED_WAITING
线程执行以下限时等待操作时进入该状态:Thread.sleep(long)
- 持有锁时调用
Object.wait(long)
- 调用其他线程的
join(long)
LockSupport.parkNanos(long)
或parkUntil(long)
-
TIMED_WAITING → RUNNABLE
唤醒条件:- 等待时间超时
- 其他线程提前唤醒(如
notify()
、unpark()
等) - 线程被中断(
interrupt()
)
-
RUNNABLE → TERMINATED
线程的run()
方法执行完毕,或因未捕获的异常终止。 -
其他状态 → TERMINATED
线程在任何状态下被强制终止(如调用已废弃的stop()
方法),但不推荐使用这种方式,可能导致资源泄露。
特殊情况:中断(interrupt)对状态的影响
线程的interrupt()
方法可以中断处于WAITING或TIMED_WAITING状态的线程,使其抛出InterruptedException
并转换为RUNNABLE状态:
Thread thread = new Thread(() -> {
try {
Thread.sleep(10000); // 进入TIMED_WAITING
} catch (InterruptedException e) {
System.out.println("线程被中断");
}
});
thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // TIMED_WAITING
thread.interrupt(); // 中断线程
Thread.sleep(100);
System.out.println(thread.getState()); // RUNNABLE(若已处理异常)
四、常见状态转换场景示例
场景1:同步代码块导致的BLOCKED转换
Object lock = new Object();
// 线程A获取锁后休眠
Thread threadA = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(2000); // 持有锁休眠(TIMED_WAITING)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 线程B尝试获取同一把锁
Thread threadB = new Thread(() -> {
synchronized (lock) { // 此时锁被A持有,进入BLOCKED
System.out.println("线程B获得锁");
}
});
threadA.start();
Thread.sleep(100); // 确保A先启动
threadB.start();
Thread.sleep(100);
System.out.println("threadA状态:" + threadA.getState()); // TIMED_WAITING
System.out.println("threadB状态:" + threadB.getState()); // BLOCKED
// 2秒后,threadA释放锁,threadB获得锁进入RUNNABLE
Thread.sleep(2000);
System.out.println("threadB状态:" + threadB.getState()); // RUNNABLE(或已TERMINATED)
场景2:wait()与notify()的状态转换
Object lock = new Object();
Thread waiter = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("等待唤醒...");
lock.wait(); // 释放锁,进入WAITING
System.out.println("被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread notifier = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(1000); // 等待waiter进入WAITING
lock.notify(); // 唤醒waiter
System.out.println("已发送唤醒通知");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
waiter.start();
Thread.sleep(100);
System.out.println("waiter状态:" + waiter.getState()); // WAITING
notifier.start();
Thread.sleep(1000);
System.out.println("waiter状态:" + waiter.getState()); // RUNNABLE(重新竞争锁后)
五、总结
Java线程的6种状态构成了一个完整的生命周期,从新建(NEW)到启动(RUNNABLE),经过各种等待状态(BLOCKED、WAITING、TIMED_WAITING),最终到终止(TERMINATED)。理解这些状态及其转换规则,对于诊断并发问题、优化多线程程序至关重要。
关键要点:
- BLOCKED仅与
synchronized
锁竞争相关 - WAITING和TIMED_WAITING的区别在于是否有超时机制
- 线程状态转换反映了线程对资源(CPU、锁)的获取与释放过程
- 避免使用已废弃的线程控制方法(如
stop()
、suspend()
),应通过中断机制或等待/通知机制安全控制线程
掌握线程状态转换,能帮助你写出更健壮的并发程序,有效解决死锁、活锁等常见并发问题。
更多推荐
所有评论(0)