Java 线程的生命周期

更新于 2025-12-29

baeldung 2024-01-08

1. 引言

在本文中,我们将详细探讨 Java 中的一个核心概念——线程的生命周期

我们会使用一张简明的示意图,并辅以实用的代码片段,帮助大家更好地理解线程在其执行过程中所经历的各种状态。

若要开始了解 Java 中的线程,可以先阅读这篇关于如何创建线程的文章。

2. Java 中的多线程

Java 语言中的多线程机制由 Thread(线程) 这一核心概念驱动。在线程的整个生命周期中,它会经历多种不同的状态:

线程的生命周期

3. Java 中线程的生命周期

java.lang.Thread 类中包含一个静态的 State 枚举类型,用于定义线程可能处于的各种状态。在任意时刻,线程只能处于以下六种状态之一:

  • NEW(新建):已创建但尚未启动执行的线程。
  • RUNNABLE(可运行):正在运行或已准备好运行,但正在等待系统分配资源。
  • BLOCKED(阻塞):正在等待获取监视器锁,以便进入或重新进入某个同步代码块/方法。
  • WAITING(等待):无限期地等待其他线程执行特定操作。
  • TIMED_WAITING(计时等待):在指定时间内等待其他线程执行特定操作。
  • TERMINATED(终止):已完成执行(正常结束或异常终止)。

以上所有状态均已在上图中体现;接下来我们将逐一详细说明。

3.1. NEW(新建)

一个 NEW 状态的线程(也称为“新生线程”)是指已经创建但尚未调用 start() 方法启动的线程。只有当我们调用其 start() 方法后,它才会离开此状态。

以下代码片段展示了一个处于 NEW 状态的新建线程:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
System.out.println(t.getState());

由于我们尚未启动该线程,因此 t.getState() 的输出为:

NEW

3.2. RUNNABLE(可运行)

当我们创建一个新线程并调用其 start() 方法后,线程会从 NEW 状态转为 RUNNABLE 状态。处于该状态的线程要么正在运行,要么已准备好运行,只是在等待系统分配 CPU 资源。

在多线程环境中,JVM 的 线程调度器(Thread-Scheduler) 会为每个线程分配固定的时间片。线程运行一段时间后,会主动让出控制权,交由其他处于 RUNNABLE 状态的线程执行。

例如,在前面的代码中添加 t.start() 并查看其当前状态:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
System.out.println(t.getState());

这段代码很可能输出:

RUNNABLE

注意:在此示例中,当执行到 t.getState() 时,线程可能已被调度器立即执行完毕。因此,有时也可能看到其他状态(如 TERMINATED),这取决于线程调度的具体时机。

3.3. BLOCKED(阻塞)

当线程试图进入一个被其他线程锁定的同步代码块或方法时,它将进入 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());
        System.exit(0);
    }
}

class DemoBlockedRunnable implements Runnable {
    @Override
    public void run() {
        commonResource();
    }
    
    public static synchronized void commonResource() {
        while(true) {
            // 无限循环模拟长时间处理
            // 't1' 不会退出此方法
            // 当 't2' 尝试进入时会被阻塞
        }
    }
}

代码说明:

  • 创建了两个线程 t1t2
  • t1 启动后进入 synchronized 修饰的 commonResource() 方法,该方法一次只允许一个线程访问;
  • t1 在方法内陷入无限循环,模拟长时间占用;
  • t2 启动并尝试进入同一方法时,由于锁已被 t1 持有,t2 将进入 BLOCKED 状态;
  • 此时调用 t2.getState(),输出为:
BLOCKED

3.4. WAITING(等待)

当一个线程无限期地等待另一个线程执行特定操作时,它就处于 WAITING 状态。根据 Java 官方文档,以下三种方法会使线程进入此状态:

  • object.wait()
  • thread.join()
  • LockSupport.park()

注意:wait()join() 在此处不带超时参数;带超时的情况属于下一节的 TIMED_WAITING

现在,我们通过以下代码复现 WAITING 状态:

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(); // t1 在此处等待 t2 结束
        } 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());
    }
}

逻辑说明:

  • 创建并启动 t1
  • t1 内部创建并启动 t2
  • t1 调用 t2.join(),从而进入 WAITING 状态,直到 t2 执行完毕;
  • t2 中打印 t1 的状态;

输出结果为:

WAITING

3.5. TIMED_WAITING(计时等待)

当线程在指定时间内等待另一个线程执行特定操作时,它处于 TIMED_WAITING 状态。

根据 Java 文档,以下五种方式可使线程进入此状态:

  • Thread.sleep(long millis)
  • Object.wait(long timeout)wait(long timeout, int nanos)
  • Thread.join(long millis)
  • LockSupport.parkNanos()
  • LockSupport.parkUntil()

下面是一个快速复现 TIMED_WAITING 状态的示例:

public class TimedWaitingState {
    public static void main(String[] args) throws InterruptedException {
        DemoTimeWaitingRunnable runnable = new DemoTimeWaitingRunnable();
        Thread t1 = new Thread(runnable);
        t1.start();
        
        // 给线程调度器足够时间启动 t1
        Thread.sleep(1000);
        System.out.println(t1.getState());
    }
}

class DemoTimeWaitingRunnable implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(5000); // 睡眠 5 秒
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}

这里,t1 启动后立即进入 5 秒的睡眠状态,因此在主线程中打印其状态时,输出为:

TIMED_WAITING

3.6. TERMINATED(终止)

TERMINATED 是线程的最终状态,表示线程已正常执行完毕因异常而终止

我们另有一篇文章专门讨论如何正确停止线程

下面的代码演示如何使线程进入 TERMINATED 状态:

public class TerminatedState implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new TerminatedState());
        t1.start();
        // 给 t1 足够时间完成执行
        Thread.sleep(1000);
        System.out.println(t1.getState());
    }
    
    @Override
    public void run() {
        // 无任何处理逻辑,立即结束
    }
}

由于 t1run() 方法为空,启动后几乎立即结束。主线程休眠 1 秒后检查其状态,输出为:

TERMINATED

此外,我们还可以使用 isAlive() 方法判断线程是否仍在运行。例如:

Assert.assertFalse(t1.isAlive());

该方法返回 false,说明线程已死亡。简单来说,只有当线程已启动且尚未终止时,isAlive() 才返回 true

4. 结论

在本教程中,我们深入学习了 Java 线程的完整生命周期,详细介绍了 Thread.State 枚举定义的六种状态,并通过简洁的代码示例复现了每一种状态。

尽管这些代码在大多数机器上会产生一致的输出,但在某些特殊情况下(如线程调度时机不同),可能会观察到不同的结果,因为线程调度器的确切行为是不可预测的