Java线程生命周期探秘:六种状态的转换与核心机制
目录
一、线程的状态图
二、状态详细说明
1. 初始状态(NEW)
2.1. 就绪状态(RUNNABLE 之 READY)
2.2. 运行中状态(RUNNABLE 之 RUNNING)
3. 阻塞状态(BLOCKED)
4. 等待状态(WAITING)
5. 超时等待状态(TIMED_WAITING)
6. 终止状态(TERMINATED)
三、等待队列
四、同步队列状态
五、几个方法的比较
六、总结
在 Java 中,线程是并发编程的核心概念之一。理解线程的状态及其切换条件对于编写高效、正确的并发程序至关重要。本文将详细讲解 Java 线程的几种状态、每种状态的含义以及状态之间的切换条件。
一、线程的状态图
二、状态详细说明
1. 初始状态(NEW)
线程对象已经被创建,但尚未调用 start() 方法启动线程,这意味着线程处于未启动状态且不会被执行。只有当调用了 start() 方法之后,线程才会进入 RUNNABLE 状态,开始准备运行。
2.1. 就绪状态(RUNNABLE 之 READY)
一旦线程被启动并获得了 start() 方法的调用,它便进入了就绪状态,准备好由CPU调度执行。在这一状态下,线程虽然已启动,但还需等待获得CPU时间片才能真正执行任务。如果线程获得了CPU时间片,则会进入运行中状态;若需等待某些资源或锁,则可能转入阻塞状态或等待状态。
2.2. 运行中状态(RUNNABLE 之 RUNNING)
线程在获取了CPU时间片后,即进入了运行中状态,此时它正在执行其预定的任务。在这一过程中,线程可能会因为时间片耗尽而重新回到就绪状态等待下一次调度,或者由于执行了I/O操作等需要等待的情况而进入阻塞状态,亦或是通过调用 wait() 方法进入等待状态。
3. 阻塞状态(BLOCKED)
当一个线程试图进入同步代码块或方法但所需锁已被其他线程持有时,该线程将进入阻塞状态。在此状态下,线程无法继续执行直至所需的锁变为可用。一旦锁被释放,线程即可从阻塞状态返回到 RUNNABLE 状态,继续竞争执行机会。
4. 等待状态(WAITING)
线程可通过调用如 Object.wait()、Thread.join() 或 LockSupport.park() 等方法进入无限期等待状态,等待另一线程的显式唤醒。在这种状态下,除非被其他线程通过 notify() 或 notifyAll() 方法唤醒,或者被中断抛出 InterruptedException,否则线程将持续保持等待。
5. 超时等待状态(TIMED_WAITING)
不同于无限期等待,超时等待状态允许线程在指定时间内等待其他线程的操作完成,例如通过调用 Thread.sleep(long millis) 或 Object.wait(long timeout)。当设定的时间到期后,线程将自动恢复到 RUNNABLE 状态;当然,也可以提前通过其他线程的 notify() 或 notifyAll() 方法唤醒。
6. 终止状态(TERMINATED)
当线程完成了其 run() 方法中的所有任务,或因未捕获的异常而终止时,它将进入终止状态。到达这一状态后,线程不再具备运行能力,意味着它的生命周期已经结束。
以下是一个完整的代码示例,展示了 Java 线程的六种状态(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING 和 TERMINATED)以及它们之间的转换关系。通过这个示例,您可以更直观地理解线程状态的变化。
代码示例
public class ThreadStateExample {
public static void main(String[] args) throws InterruptedException {
// 1. 初始状态 (NEW)
Thread thread1 = new Thread(() -> {
System.out.println("Thread-1 is running...");
});
System.out.println("Thread-1 state after creation: " + thread1.getState());
// 启动线程,进入 RUNNABLE 状态
thread1.start();
System.out.println("Thread-1 state after start(): " + thread1.getState());
// 2. 运行中状态 (RUNNABLE)
Thread.sleep(100); // 给线程一些时间运行
System.out.println("Thread-1 state during execution: " + thread1.getState());
// 3. 阻塞状态 (BLOCKED)
Object lock = new Object();
Thread thread2 = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(2000); // 模拟长时间占用锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread3 = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread-3 acquired the lock.");
}
});
thread2.start(); // 先启动 thread2 占用锁
Thread.sleep(100); // 确保 thread2 已经获取锁
thread3.start(); // 启动 thread3 尝试获取锁
Thread.sleep(100); // 等待 thread3 进入阻塞状态
System.out.println("Thread-3 state while waiting for lock: " + thread3.getState());
// 4. 等待状态 (WAITING)
Thread thread4 = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(); // 等待被唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread4.start();
Thread.sleep(100); // 确保 thread4 进入 WAITING 状态
System.out.println("Thread-4 state after calling wait(): " + thread4.getState());
// 唤醒等待中的线程
synchronized (lock) {
lock.notifyAll();
}
Thread.sleep(100); // 等待 thread4 被唤醒
System.out.println("Thread-4 state after notifyAll(): " + thread4.getState());
// 5. 超时等待状态 (TIMED_WAITING)
Thread thread5 = new Thread(() -> {
try {
Thread.sleep(3000); // 线程进入 TIMED_WAITING 状态
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread5.start();
Thread.sleep(100); // 确保 thread5 进入 TIMED_WAITING 状态
System.out.println("Thread-5 state during sleep: " + thread5.getState());
// 6. 终止状态 (TERMINATED)
Thread.sleep(3200); // 等待所有线程执行完毕
System.out.println("Thread-1 state after termination: " + thread1.getState());
System.out.println("Thread-5 state after termination: " + thread5.getState());
}
}
输出结果示例
Thread-1 state after creation: NEW
Thread-1 state after start(): RUNNABLE
Thread-1 state during execution: RUNNABLE
Thread-3 state while waiting for lock: BLOCKED
Thread-4 state after calling wait(): WAITING
Thread-4 state after notifyAll(): RUNNABLE
Thread-5 state during sleep: TIMED_WAITING
Thread-1 state after termination: TERMINATED
Thread-5 state after termination: TERMINATED
示例解析
1. 初始状态(NEW):
创建线程对象 thread1,此时线程尚未调用 start() 方法,处于 NEW 状态。
2. 就绪状态和运行中状态(RUNNABLE):
调用 thread1.start() 后,线程进入 RUNNABLE 状态。
在 JVM 中,线程会交替处于 READY(等待 CPU 时间片)和 RUNNING(正在执行任务)两种子状态。
3. 阻塞状态(BLOCKED):
thread2 占用了 lock 锁,thread3 尝试获取锁但无法成功,因此进入 BLOCKED 状态。
4. 等待状态(WAITING):
thread4 调用了 lock.wait(),释放锁并进入 WAITING 状态。
使用 lock.notifyAll() 唤醒等待队列中的线程后,线程重新进入同步队列争夺锁。
5. 超时等待状态(TIMED_WAITING):
thread5 调用了 Thread.sleep(3000),进入 TIMED_WAITING 状态,直到睡眠时间结束或被中断。
6. 终止状态(TERMINATED):
当线程的任务完成或因异常退出后,线程进入 TERMINATED 状态,生命周期结束。
三、等待队列
等待队列是线程在调用 wait() 方法后进入的一个队列。当线程调用某个对象的 wait() 方法时,它会释放该对象的锁,并进入与该对象关联的等待队列,线程状态从 RUNNABLE 转变为 WAITING。线程会一直停留在等待队列中,直到其他线程通过调用该对象的 notify() 或 notifyAll() 方法将其唤醒。
特点:
线程在调用 wait() 方法时必须持有对象的锁。
调用 wait() 后,线程释放锁并进入等待队列。
唤醒后,线程需要重新竞争锁,只有获取到锁后才能继续执行。
唤醒机制:
notify():随机唤醒等待队列中的一个线程。
notifyAll():唤醒等待队列中的所有线程,这些线程将重新进入同步队列争夺锁。
线程同步示例
以下是一个典型的线程同步示例,展示了如何使用 wait() 和 notifyAll() 来实现线程间的协作:
public class WaitNotifyExample {
// 共享资源
private static final Object lock = new Object();
private static boolean flag = false;
public static void main(String[] args) {
// 线程1:等待条件满足
Thread thread1 = new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 进入等待队列...");
while (!flag) {
try {
lock.wait(); // 进入 WAITING 状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 条件满足,继续执行...");
}
}, "Thread-1");
// 线程2:修改条件并唤醒等待线程
Thread thread2 = new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 修改条件...");
flag = true;
lock.notifyAll(); // 唤醒所有等待线程
System.out.println(Thread.currentThread().getName() + " 已唤醒所有等待线程...");
}
}, "Thread-2");
// 启动线程
thread1.start();
try {
Thread.sleep(1000); // 确保线程1先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
示例运行过程分析
1. 线程1启动:
线程1进入同步代码块,检查 flag 的值为 false。
线程1调用 lock.wait(),释放锁并进入等待队列,线程状态变为 WAITING。
2. 线程2启动:
线程2进入同步代码块,修改 flag 的值为 true。
线程2调用 lock.notifyAll(),唤醒等待队列中的所有线程(此时只有线程1)。
线程2退出同步代码块,释放锁。
3. 线程1被唤醒:
线程1从等待队列中被唤醒,重新进入同步队列,尝试获取锁。
获取锁后,线程1继续执行,发现 flag 已经为 true,退出循环并打印消息。
输出结果示例
Thread-1 进入等待队列...
Thread-2 修改条件...
Thread-2 已唤醒所有等待线程...
Thread-1 条件满足,继续执行...
总结
通过上述示例可以看出:
wait() 和 notifyAll() 是线程间协作的重要工具。
等待队列和同步队列的配合使用,可以实现线程的高效调度和资源共享。
使用 synchronized 关键字确保了对共享资源的安全访问,避免了竞态条件的发生。
四、同步队列状态
同步队列在线程尝试获取锁但锁当前不可用时发挥关键作用。当一个线程试图进入同步代码块或方法,而相应的对象锁已被其他线程持有,该线程就会被放置于同步队列中,并且其状态变为 BLOCKED(阻塞)。在同步队列中的线程会等待锁的释放,一旦持有锁的线程释放了锁,系统会选择同步队列中的一个线程来获取这个锁并继续执行。
以下是关于同步队列状态的一些重要点:
BLOCKED 状态:表示线程正在等待获取一个锁以进入同步代码区。处于此状态的线程不会使用CPU资源,它们只是静止地等待,直到锁可用。
锁的竞争:多个线程可能同时竞争同一个锁,这些线程都会被加入到同步队列中。锁的分配通常遵循公平性原则(取决于具体的JVM实现),这意味着等待时间最长的线程可能会优先获得锁。
状态转换:一旦线程获得了所需的锁,它就会从 BLOCKED 状态转变为 RUNNABLE 状态,并开始执行同步代码块或方法。如果线程执行完毕或调用了 wait() 方法,则会释放锁,允许同步队列中的其他线程获得锁。
理解同步队列和线程的状态转换对于开发高效并发程序至关重要,因为它直接影响到程序的性能和线程之间的交互方式。正确管理锁的获取与释放可以避免死锁和减少不必要的线程阻塞时间。
五、几个方法的比较
六、总结
在Java中,线程的生命周期包括六种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,这些状态分别代表了线程从新建到最终终止过程中所处的不同阶段。理解每种状态的含义及其转换条件,如新建状态表示线程已被实例化但尚未调用start()方法,RUNNABLE状态意味着线程正在JVM中执行但可能等待系统资源,BLOCKED状态为线程等待获取锁以进入同步代码区,WAITING和TIMED_WAITING涉及线程等待其他线程的动作而处于等待或限时等待,TERMINATED则表示线程已完成执行或因异常终止,对于编写高效且可靠的多线程程序至关重要。