Java 线程生命周期

2025年17月8日 | 阅读8分钟

在Java中,线程在其生命周期中会经历几个不同的状态,这些状态在java.lang.Thread.State 类中定义。所有线程状态都是Enum常量。一个线程在任何给定时间只能处于一种状态。

这些状态是虚拟机状态,并不反映任何操作系统线程状态。线程可能处于以下状态之一:

线程状态描述
NEW尚未启动的线程处于此状态。
RUNNABLE在Java虚拟机中执行的线程处于此状态。
BLOCKED等待监视器锁的线程处于此状态。
WAITING无限期等待另一个线程执行特定操作的线程处于此状态。
TIMED_WAITING等待另一个线程在指定等待时间内执行操作的线程处于此状态。
TERMINATED已退出的线程处于此状态。

线程状态

NEW:当创建Thread实例但尚未调用start()方法时,线程处于NEW状态。在此阶段,线程尚未“存活”,也不能被线程调度器运行。

RUNNABLE:已准备好运行并调用了start()方法的线程,将从NEW状态转变为RUNNABLE状态。在RUNNABLE状态下,线程可能正在运行,也可能在任何给定时间点准备运行。由线程调度器负责为线程提供运行时间。

实现多线程的程序会为每个独立线程分配固定的时间片。每个线程都运行一小段时间,当分配的时间片用完时,线程会主动将CPU让给其他线程,以便其他线程也能运行其时间片。当发生这种情况时,所有愿意运行、等待轮到自己运行的线程都处于RUNNABLE状态。在RUNNABLE状态下,有一个队列,线程排队等待。

当线程获得CPU时,它将从RUNNABLE状态转变为运行状态。通常,线程状态最常见的变化是从RUNNABLE到运行,再回到RUNNABLE。

BLOCKED:当线程等待获取另一个线程持有的监视器锁时,它会进入BLOCKED状态。这通常发生在线程尝试进入同步块或方法但锁不可用时。

例如,一个线程(比如A)可能想从打印机打印一些数据。但是,同时,另一个线程(比如B)正在使用打印机打印一些数据。因此,线程A必须等待线程B使用完打印机。因此,线程A处于BLOCKED状态。处于BLOCKED状态的线程无法执行任何操作,因此永远不会消耗CPU的任何周期。因此,我们可以说线程A保持空闲,直到线程调度器重新激活处于等待或BLOCKED状态的线程A。

当主线程调用join()方法时,可以说主线程处于WAITING状态。主线程然后等待子线程完成它们的任务。当子线程完成工作后,会向主线程发送通知,这会将线程从WAITING状态重新移至RUNNABLE状态。

如果有很多线程处于等待或阻塞状态,那么由线程调度器决定选择哪个线程,拒绝哪个线程,然后选中的线程将获得运行的机会。

WAITING:当线程无限期地等待另一个线程执行特定操作时,它会转换为WAITING状态。这可能发生在调用没有超时参数的Object.wait()方法或Thread.join()方法时。线程将保持此状态,直到被另一个线程通知(例如,通过Object.notify()或Object.notifyAll())或被join的线程完成。

WAITING和TIMED_WAITING之间的关键区别在于时间限制。Waiting没有时间限制,而timed waiting有时间限制。调用以下方法的线程将达到timed waiting状态。

  • 睡眠
  • 带超时的join
  • 带超时的wait
  • parkUntil
  • parkNanos

它表示线程已终止或死亡的最终状态。已终止的线程意味着它已完成其执行。

TIMED_WAITING:有时,等待会导致饥饿。例如,一个线程(名为A)进入了代码的关键部分,并且不愿离开该关键部分。在这种情况下,另一个线程(名为B)将永远等待,这会导致饥饿。为了避免这种情况,给线程B一个timed WAITING状态。因此,线程在WAITING状态下停留一段时间,而不是永远。timed waiting的一个真实示例是我们对特定线程调用sleep()方法。sleep()方法将线程置于TIMED WAIT状态。时间耗尽后,线程会醒来并从之前离开的地方继续执行。

TERMINATED:线程由于以下原因达到TERMINATED状态

  • 当线程完成其工作后,它会正常退出或终止。
  • 异常终止:当发生一些异常事件,如未处理的异常或段错误时发生。

已终止的线程意味着该线程已不再系统中。换句话说,该线程已死亡,并且无法重新激活(杀死后激活)已死的线程。

下图显示了线程生命周期中涉及的不同状态。

Java thread life cycle

状态之间的转换

  • new → start() → runnable
  • runnable → 调度器选择 → running
  • running → wait()/join() → waiting
  • running → sleep()/join(timeout) → timed waiting
  • running → blocked → 尝试获取锁时
  • any state → finished/error → terminated

示例:线程状态

以下Java程序展示了上面定义的线程的一些状态。

示例

编译并运行

输出

The state of thread t1 after spawning it - NEW
The state of thread t1 after invoking the method start() on it - RUNNABLE
The state of thread t2 after spawning it - NEW
the state of thread t2 after calling the method start() on it - RUNNABLE
The state of thread t1 while it invoked the method join() on thread t2 -TIMED_WAITING
The state of thread t2 after invoking the method sleep() on it - TIMED_WAITING
The state of thread t2 when it has completed it's execution - TERMINATED

解释:每当创建一个新线程时,该线程就处于new状态。当在线程上调用start()方法时,线程调度器会将该线程移至runnable状态。每当在任何线程实例上调用join()方法时,执行该语句的当前线程必须等待该线程完成其执行,即移至terminated状态。因此,在控制台打印最终的print语句之前,程序会在线程t2上调用join()方法,使线程t1等待,直到线程t2完成执行,从而线程t2进入terminated或dead状态。线程t1进入WAITING状态,因为它正在等待线程t2完成其执行,因为它已经在线程t2上调用了join()方法。

线程生命周期选择题

1. 哪种方法可以用来中断处于TIMED_WAITING或WAITING状态的线程,使其转换为RUNNABLE状态?

  1. notify()
  2. interrupt()
  3. join()
  4. start()

答案:B)

解释:interrupt()方法可用于中断处于TIMED_WAITING或WAITING状态的线程,通过抛出InterruptedException使其转换为RUNNABLE状态。


2. 以下哪种方法可用于使当前正在运行的线程将执行权让给具有相同或更高优先级的另一个线程?

  1. sleep()
  2. yield()
  3. wait()
  4. notify()

答案:B)

解释:yield()方法用于使当前正在运行的线程暂停,并允许其他具有相同或更高优先级的线程执行。


3. 如果一个线程调用另一个线程的join()方法会发生什么?

  1. 它进入BLOCKED状态
  2. 它进入WAITING状态,直到另一个线程完成
  3. 它进入TIMED_WAITING状态固定时间
  4. 它在状态不变的情况下继续运行

答案:B)

解释:当一个线程调用另一个线程的join()方法时,它会进入WAITING状态,直到另一个线程完成其执行。


4. 哪种方法用于请求线程停止执行,并且可以被线程安全地忽略?

  1. stop()
  2. suspend()
  3. interrupt()
  4. terminate()

答案:C)

解释:interrupt()方法请求线程停止执行,但如果线程没有被设计为处理中断,则可以安全地忽略它。


5. 当一个线程获得一个对象的锁时,如果另一个线程尝试获取同一个锁,它会进入什么状态?

  1. TIMED_WAITING
  2. WAITING
  3. BLOCKED
  4. RUNNABLE

答案:C)

解释:当一个线程获得一个对象的锁时,另一个尝试获取同一个锁的线程将进入BLOCKED状态,直到锁被释放。


下一个主题如何创建线程