Java线程生命周期及状态详解
1. 简介
本文将详细讨论Java中线程的生命周期。我们将使用图示和代码片段来更好地理解线程在执行过程中的各个状态。
要了解Java中的线程,创建线程是一个好的起点。
2. Java中的多线程
在Java语言中,多线程由线程这一核心概念驱动。在线程的生命周期中,它们会经历各种状态:
3. Java中线程的生命周期
java.lang.Thread
类包含一个静态的 State
枚举,定义了线程可能的状态。在任何给定时刻,线程只能处于这些状态之一:
- NEW:新创建的线程,还未开始执行。
- RUNNABLE:包括就绪和运行两种状态。
- BLOCKED:等待获取一个监视器锁,以便进入或重新进入同步块/方法。
- WAITING:无限期等待另一个线程执行特定操作。
- TIMED_WAITING:在指定时间内等待另一个线程执行特定操作。
- TERMINATED:线程执行完毕。
下面将详细讨论这些状态。
3.1 新建状态 (New)
一个新建的线程已创建但未开始。调用 start()
方法之前,它保持在该状态。
Runnable runnable = () -> {};
Thread t = new Thread(runnable);
System.out.println(t.getState()); // 输出 NEW
3.2 运行状态 (Runnable)
调用 start()
方法后,线程从 NEW 转为 RUNNABLE 状态。RUNNABLE状态包括就绪(ready)和运行中(running)两种状态。
/**
* 线程状态转换的Demo(用于演示状态转换)
*
* @author LiWenzhang
*/
public class ThreadStateTransitionDemo {
public static void main(String[] args) throws InterruptedException {
demonstrateNewToRunnableToTerminated();
}
// 新建状态 -> 就绪状态 -> 运行状态 -> 就绪状态 -> 终止状态 (NEW -> RUNNABLE -> TERMINATED)
public static void demonstrateNewToRunnableToTerminated() throws InterruptedException {
System.out.println("Demo: 新建状态 -> 就绪状态 -> 运行状态 -> 就绪状态 -> 终止状态");
Thread thread1 = new Thread(() -> {
System.out.println("Thread1 is running");
Thread.yield(); // 让出CPU资源,进入就绪状态
System.out.println("Thread1 resumed running");
}, "Thread1");
System.out.println(thread1.getName() + " 状态: " + thread1.getState()); // NEW
thread1.start();
System.out.println(thread1.getName() + " 状态: " + thread1.getState()); // RUNNABLE
thread1.join();
System.out.println(thread1.getName() + " 状态: " + thread1.getState()); // TERMINATED
}
}
3.2.1 就绪 (Ready)
当一个线程在准备运行但尚未获得CPU时间片时,处于就绪状态。它等待系统调度分配CPU资源。
注意,在多线程环境中,线程调度器(JVM的一部分)为每个线程分配固定时间,因此线程会在RUNNABLE状态下等待调度。不能保证在调用 t.getState()
时,线程一定处于就绪状态,因为它可能已经获得了CPU资源并开始运行。
3.2.2 运行 (Running)
当一个就绪状态的线程获得了CPU时间片后,它进入运行状态。
虽然Java没有提供直接检测线程是否在运行的方法,但是在特定条件下,我们可以推断出线程处于运行状态,例如在执行代码逻辑时。
3.3 阻塞状态 (Blocked)
当线程等待获取一个监视器锁时,进入 BLOCKED 状态。以下代码演示了这一状态:
public class BlockedState {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DemoBlockedRunnable());
Thread t2 = new Thread(new DemoBlockedRunnable());
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println(t2.getState()); // 输出 BLOCKED
System.exit(0);
}
}
class DemoBlockedRunnable implements Runnable {
@Override
public void run() {
commonResource();
}
public static synchronized void commonResource() {
while (true) {
// 模拟长时间处理
}
}
}
3.4 等待状态 (Waiting)
线程在等待另一个线程执行特定操作时,进入 WAITING 状态。可以通过 object.wait()
、thread.join()
或 LockSupport.park()
方法进入该状态。
public class WaitingState implements Runnable {
public static Thread t1;
public static void main(String[] args) {
t1 = new Thread(new WaitingState());
t1.start();
}
public void run() {
Thread t2 = new Thread(new DemoWaitingStateRunnable());
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
class DemoWaitingStateRunnable implements Runnable {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
System.out.println(WaitingState.t1.getState()); // 输出 WAITING
}
}
3.5 超时等待状态 (Timed Waiting)
线程在等待指定时间后自动唤醒时,进入 TIMED_WAITING 状态。可以通过 thread.sleep(long millis)
、wait(int timeout)
、thread.join(long millis)
等方法进入该状态。
public class TimedWaitingState {
public static void main(String[] args) throws InterruptedException {
DemoTimeWaitingRunnable runnable = new DemoTimeWaitingRunnable();
Thread t1 = new Thread(runnable);
t1.start();
Thread.sleep(1000);
System.out.println(t1.getState()); // 输出 TIMED_WAITING
}
}
class DemoTimeWaitingRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
3.6 终止状态 (Terminated)
当线程完成执行或因异常退出时,进入 TERMINATED 状态。
public class TerminatedState implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TerminatedState());
t1.start();
Thread.sleep(1000);
System.out.println(t1.getState()); // 输出 TERMINATED
}
@Override
public void run() {
// 无操作
}
}
可以使用 isAlive()
方法来判断线程是否仍在运行:
Assert.assertFalse(t1.isAlive());
4. 线程状态转换
4.1. 新建状态 -> 就绪状态 -> 运行状态 -> 就绪状态 -> 终止状态 (NEW -> RUNNABLE -> TERMINATED)
在这个示例中,Thread1
从新建状态(NEW)开始,调用start()
方法后进入可运行状态(RUNNABLE),中途让出CPU时间变为就绪后获取CPU时间继续执行,执行完毕后进入终止状态(TERMINATED)。
4.2. 等待状态 (WAITING)
Thread2
在同步块中调用wait()
方法进入等待状态(WAITING),直到另一个线程调用notify()
方法唤醒它。
4.3. 计时等待状态 (TIMED_WAITING)
Thread3
调用Thread.sleep(1000)
进入计时等待状态(TIMED_WAITING),在指定时间后自动唤醒。
4.4. 阻塞状态 (BLOCKED)
Thread4
和Thread5
尝试进入同步块,Thread4
获得锁并执行,Thread5
进入阻塞状态(BLOCKED)等待锁释放。
4.5. 停顿和唤醒 (Park and Unpark)
Thread6
调用LockSupport.park()
进入停顿状态(WAITING),直到调用LockSupport.unpark(thread6)
将其唤醒。
package cn.lee.thread;
import java.util.concurrent.locks.LockSupport;
/**
* 线程状态转换的Demo(用于演示状态转换)
*
* @author LiWenzhang
*/
public class ThreadStateTransitionDemo {
public static void main(String[] args) throws InterruptedException {
demonstrateNewToRunnableToTerminated();
demonstrateWaitingState();
demonstrateTimedWaitingState();
demonstrateBlockedState();
demonstrateParkAndUnpark();
}
// 新建状态 -> 就绪状态 -> 运行状态 -> 就绪状态 -> 终止状态 (NEW -> RUNNABLE -> TERMINATED)
public static void demonstrateNewToRunnableToTerminated() throws InterruptedException {
System.out.println("\nDemo: 新建状态 -> 就绪状态 -> 运行状态 -> 就绪状态 -> 终止状态");
Thread thread1 = new Thread(() -> {
System.out.println("Thread1 is running");
Thread.yield(); // 让出CPU资源,进入就绪状态
System.out.println("Thread1 resumed running");
}, "Thread1");
System.out.println(thread1.getName() + " 状态: " + thread1.getState()); // NEW
thread1.start();
System.out.println(thread1.getName() + " 状态: " + thread1.getState()); // RUNNABLE
thread1.join();
System.out.println(thread1.getName() + " 状态: " + thread1.getState()); // TERMINATED
}
// 等待状态 (WAITING)
public static void demonstrateWaitingState() throws InterruptedException {
System.out.println("\nDemo: 等待状态");
Object lock = new Object();
Thread thread2 = new Thread(() -> {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Thread2");
thread2.start();
Thread.sleep(100); // 确保 thread2 启动并进入等待状态
System.out.println(thread2.getName() + " 状态: " + thread2.getState()); // WAITING
synchronized (lock) {
lock.notify();
}
}
// 计时等待状态 (TIMED_WAITING)
public static void demonstrateTimedWaitingState() throws InterruptedException {
System.out.println("\nDemo: 计时等待状态");
Thread thread3 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread3");
thread3.start();
Thread.sleep(100); // 确保 thread3 启动并进入睡眠状态
System.out.println(thread3.getName() + " 状态: " + thread3.getState()); // TIMED_WAITING
}
// 阻塞状态 (BLOCKED)
public static void demonstrateBlockedState() throws InterruptedException {
System.out.println("\nDemo: 阻塞状态");
Object lock = new Object();
Thread thread4 = new Thread(new BlockDemo(lock), "Thread4");
Thread thread5 = new Thread(new BlockDemo(lock), "Thread5");
thread4.start();
thread5.start();
Thread.sleep(100); // 确保 thread5 尝试进入同步块
System.out.println(thread4.getName() + " 状态: " + thread4.getState()); // RUNNABLE 或 TERMINATED
System.out.println(thread5.getName() + " 状态: " + thread5.getState()); // BLOCKED
}
// 停顿和唤醒 (Park and Unpark)
public static void demonstrateParkAndUnpark() throws InterruptedException {
System.out.println("\nDemo: 停顿和唤醒");
Thread thread6 = new Thread(() -> {
System.out.println("Thread6 is parking");
LockSupport.park();
System.out.println("Thread6 is unparked");
}, "Thread6");
thread6.start();
Thread.sleep(100); // 确保 thread6 进入停顿状态
System.out.println(thread6.getName() + " 状态: " + thread6.getState()); // WAITING
LockSupport.unpark(thread6);
}
static class BlockDemo implements Runnable {
private final Object lock;
BlockDemo(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
try {
Thread.sleep(1000); // 保持锁定一段时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
5. 总结
Java线程的生命周期包含多个状态,每个状态表示线程在其生命周期中的不同阶段。理解这些状态及其转换对于编写高效的并发程序至关重要。