Java线程状态及其流转
在Java编程中,线程是一种重要的并发实体。为了更好地理解和管理多线程应用程序,我们需要清楚线程的不同状态及其流转机制。本文将详细介绍Java中线程的几种主要状态以及它们之间的转换关系。
一、线程状态概述
Java线程的生命周期有多个状态,主要包括以下几种:
- 新建状态(New)
- 就绪状态(Runnable)
- 运行状态(Running)
- 阻塞状态(Blocked)
- 等待状态(Waiting)
- 超时等待状态(Timed Waiting)
- 终止状态(Terminated)
每一种状态代表着线程在其生命周期中的不同阶段,下面我们将逐一解析每个状态以及状态之间的流转。
二、各线程状态详解
1. 新建状态(New)
当创建一个线程对象时,该线程处于新建状态,此时线程尚未开始执行。只有当你调用 start()
方法时,新建状态的线程才能进入就绪状态。
示例代码:
Thread thread = new Thread(() -> {System.out.println("线程正在执行...");
});
2. 就绪状态(Runnable)
新建状态的线程在调用 start()
方法后进入就绪状态。此时线程处于等待系统调度的状态,准备好随时启动执行。
示例代码:
thread.start(); // 线程进入就绪状态
注意:就绪状态的线程不一定在运行,它只是等待线程调度器分配CPU时间。
3. 运行状态(Running)
当线程被分配到CPU时,线程进入运行状态。此时,线程正在执行它的任务。
- 线程从就绪状态转换为运行状态是由线程调度器决定的。
- 运行状态的线程可能会因为时间片用尽、调用
wait()
、sleep()
方法或由于其他线程抢占等原因而转移到其他状态。
4. 阻塞状态(Blocked)
阻塞状态的线程是因为尝试获取锁(synchronized
)而被阻塞。当两个或多个线程需要相同的资源(例如一个对象的锁)时,只有获得锁的线程才能执行,而其他线程会被阻塞。
示例代码:
synchronized (someObject) {// 执行的代码
}
如果另一个线程已经持有 someObject
的锁,则当前线程会进入阻塞状态,直到能够获取锁为止。
5. 等待状态(Waiting)
线程进入等待状态通常是由于调用了以下方法之一:
Object.wait()
Thread.join()
LockSupport.park()
在等待状态下的线程不会被线程调度器选中执行,直到其他线程调用 notify()
、notifyAll()
或者 join()
的线程结束。
示例代码:
synchronized (someObject) {someObject.wait(); // 进入等待状态
}
6. 超时等待状态(Timed Waiting)
超时等待状态是一种特殊的等待状态,线程等待某个条件但设定了超时时间。线程会在等待时间结束后自动恢复到就绪状态。
例如,线程调用 Thread.sleep(millis)
或 Object.wait(millis)
。
示例代码:
Thread.sleep(1000); // 线程等待1秒
7. 终止状态(Terminated)
当线程的 run()
方法执行完毕,或者因为未处理异常而终止时,线程进入终止状态。此状态的线程无法再重新启动。
示例代码:
public void run() {// 执行的代码
} // 线程执行到此,进入终止状态
线程一旦进入终止状态,它的生命周期就结束了。
三、线程状态流转
下面是一个详细的表格,内容包括线程状态、状态转换、触发条件以及实例代码说明。这个表格将帮助你更清晰地理解Java中线程的状态及其转换过程。
线程状态 | 状态描述 | 状态转换 | 触发条件 | 示例代码 |
---|---|---|---|---|
新建(New) | 线程对象被创建,但尚未启动。 | 调用 start() 方法 | 调用 Thread t = new Thread() | Thread t = new Thread(); |
就绪(Runnable) | 线程已启动,准备执行,但 CPU 未分配给它。 | 由线程调度器分配 CPU 或调用 yield() | 调用 start() 后,等待 CPU 调度 | t.start(); |
运行(Running) | 线程正在执行任务。 | 调度器分配 CPU 资源,或因时间片用尽被抢占 | CPU 调度 | // 代码在 run() 方法中执行 |
阻塞(Blocked) | 线程等待获取其他线程持有的锁。 | 获取锁成功,或其他条件通知 | 当前线程调用了 synchronized ,但未获取锁 | synchronized(lock) { ... } |
等待(Waiting) | 线程等待其他线程通知。 | 由其他线程调用 notify() 或 notifyAll() | 调用 wait() 、join() | lock.wait(); |
超时等待(Timed Waiting) | 线程等待某个条件,但设定了超时时间。 | 超时结束,或被调用 notify() | 调用 Thread.sleep(millis) 或 wait(millis) | Thread.sleep(1000); |
终止(Terminated) | 线程生命周期结束。 | 无法再转换,无论是正常结束还是异常 | run() 方法执行完毕或抛出未处理的异常 | public void run() { ... } |
状态转换详细说明
-
新建(New)→ 就绪(Runnable):
- 条件:调用
start()
方法,使线程准备执行。 - 示例代码:
Thread thread = new Thread(() -> { /* 执行的任务 */ });thread.start(); // 线程从新建状态转到就绪状态
- 条件:调用
-
就绪(Runnable)→ 运行(Running):
- 条件:线程调度器将 CPU 时间片分配给线程,使其进入运行状态。
- 示例:后台执行的任务,调度器根据优先级选择线程。
-
运行(Running)→ 阻塞(Blocked):
- 条件:线程尝试访问
synchronized
块或方法时,发现已被其他线程占用。 - 示例代码:
synchronized(lock) { // 访问被锁住的资源}
- 条件:线程尝试访问
-
运行(Running)→ 等待(Waiting):
- 条件:调用
wait()
、join()
或LockSupport.park()
,进入等待状态。 - 示例代码:
synchronized (lock) { lock.wait(); // 进入等待状态,直到有其他线程调用notify}
- 条件:调用
-
运行(Running)→ 超时等待(Timed Waiting):
- 条件:调用带有时间参数的方法,如
Thread.sleep(millis)
或wait(millis)
。 - 示例代码:
Thread.sleep(1000); // 线程暂停1秒进入超时等待状态
- 条件:调用带有时间参数的方法,如
-
阻塞(Blocked)→ 就绪(Runnable):
- 条件:成功获取锁,线程重新进入就绪状态,等待 CPU 调度。
- 示例:当锁被释放后,阻塞的线程尝试获取锁,成功时转为就绪状态。
-
等待(Waiting)→ 就绪(Runnable):
- 条件:其他线程调用了
notify()
或notifyAll()
,唤醒等待线程。 - 示例代码:
// 在另一个线程synchronized (lock) { lock.notify(); // 唤醒一个等待的线程}
- 条件:其他线程调用了
-
超时等待(Timed Waiting)→ 就绪(Runnable):
- 条件:等待超时到达,线程自动恢复就绪状态。
- 示例:如上所示的
Thread.sleep
达到指定时间。
-
运行(Running)→ 终止(Terminated):
- 条件:线程执行完毕,或者抛出未捕获的异常,线程自然结束。
- 示例代码:
public void run() { // 一些代码} // 代码完成或抛出异常则终止
四、总结
了解Java线程的各个状态及其流转机制是掌握并发编程的基础。通过清晰的状态流转关系,可以帮助我们更有效地管理和调试多线程应用程序。希望本文能帮助到你在多线程编程中的学习与应用。