Java 多线程详解:状态生命周期、创建方式与终止机制

一颗松果
一颗松果
发布时间:2026-10-13| 5人浏览过
原创

深入理解 Java 多线程需掌握其生命周期状态转换与底层创建机制。本文将图解线程的五种基本状态,详细对比 Thread、Runnable 和 Callable 三种创建方式的多态表现与返回值处理,并提供基于布尔标志位的线程安全终止方案。

结果或目标:掌握多线程核心机制

多线程是 Java 并发编程的基石。理解线程的生命周期与正确的启动、销毁方式,有助于写出高效且安全的并发代码。本文将围绕线程状态的流转、不同创建方式的源码逻辑以及线程的安全退出三个维度展开。

准备条件:线程的五种基本状态

Java 线程在其生命周期中主要经历五种状态,这些状态构成了多线程调度的基础。

第1步:识别线程状态定义与图示

目的:建立对线程生命周期全貌的直观认知。

线程的生命周期可以通过一张状态流转图来概括。下图展示了各状态的名称及主要转换节点:

第2步:理解各状态的转换条件

目的:明确何时进入特定状态以及状态间的互斥关系。

  1. 新建状态 (New):通过 new 关键字创建线程对象时进入。例如:Thread t = new MyThread();。此时仅分配了内存,尚未启动。
  2. 就绪状态 (Runnable):调用线程对象的 start() 方法后进入。此时线程已获取除 CPU 以外的所有资源,等待调度。注意:start() 不保证立即执行,仅表示具备执行资格。
  3. 运行状态 (Running):CPU 调度就绪队列中的线程时进入。这是唯一能真正执行代码的状态。关键点:就绪状态是进入运行状态的唯一入口,不存在从新建直接到运行的情况。
  4. 阻塞状态 (Blocked):运行中的线程因特定原因放弃 CPU 使用权而暂停。阻塞分为三类:
    • 等待阻塞:调用 wait() 方法。
    • 同步阻塞:尝试获取 synchronized 锁失败(锁被占用)。
    • 其他阻塞:调用 sleep()、join() 或发出 I/O 请求。超时或条件满足后重新转入就绪状态。
  5. 死亡状态 (Dead):线程执行完 run() 方法或因异常退出时进入。死亡是不可逆的,不能再次调用 start()。

真实操作顺序:三种线程创建方式解析

在实际开发中,创建并启动线程主要有三种途径。它们在执行逻辑、耦合度及功能特性上存在显著差异。

第1步:继承 Thread 类

目的:快速创建轻量级任务线程。

操作方式:创建一个类继承 java.lang.Thread,重写 run() 方法定义任务,实例化后调用 start()。

class MyThread extends Thread {
 private int i = 0;

 @Override
 public void run() {
 for (i = 0; i < 100; i++) {
  System.out.println(Thread.currentThread().getName() + " " + i);
 }
 }
}

public class Test { 
 public static void main(String[] args) {
 // ... 主线程逻辑 ...
 MyThread t = new MyThread();
 t.start(); // 进入就绪状态
 } 
}

注意:每个线程对象只能启动一次,重复调用 start() 将抛出 IllegalThreadStateException。

第2步:实现 Runnable 接口

目的:解耦任务逻辑与线程控制,支持多个线程共享同一任务资源。

操作方式:创建类实现 Runnable 接口,重写 run()。将其实例作为目标传入 Thread 构造函数。

class MyRunnable implements Runnable {
 private int i = 0;

 @Override
 public void run() {
 for (i = 0; i < 100; i++) {
  System.out.println(Thread.currentThread().getName() + " " + i);
 }
 }
}

// 启动方式
Runnable task = new MyRunnable();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task); // 两个线程共享同一个 Runnable 实例
t1.start();
t2.start();

第3步:使用 Callable 和 FutureTask

目的:需要获取线程执行返回结果或处理受检异常。

操作方式:实现 Callable<V> 接口,重写带有返回值的方法 call()。使用 FutureTask 包装 Callable 对象,再将 FutureTask 作为 Target 创建线程。

class MyCallable implements Callable<Integer> {
 @Override
 public Integer call() throws Exception {
 int sum = 0;
 for (int i = 0; i < 100; i++) sum += i;
 return sum;
 }
}

// 启动与获取结果
Callable<Integer> callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();

try {
 Integer result = futureTask.get(); // 阻塞直到获取结果
 System.out.println("Sum: " + result);
} catch (InterruptedException | ExecutionException e) {
 e.printStackTrace();
}

原理验证:Thread 与 Runnable 的多态差异

读者常混淆当同时继承 Thread 和实现 Runnable 时的执行主体。以下通过源码逻辑进行澄清。

第1步:观察源码层级关系

Thread 类本身实现了 Runnable 接口。其内部的 run() 方法逻辑如下:


@Override
public void run() {
 if (target != null) {
  target.run(); // 如果指定了 Runnable Target,则执行 Target 的 run
 }
}

这意味着,如果构造 Thread 时传入了 Runnable,默认执行的是该 Runnable 的逻辑。

第2步:多态覆盖场景

若自定义一个类既继承 Thread 又接受 Runnable 参数:

class MyThread extends Thread {
 private Runnable runnable;
 
 public MyThread(Runnable runnable){
 super(runnable);
 this.runnable = runnable;
 }

 @Override
 public void run() {
 // 此处为重写的逻辑,优先级高于父类默认行为
 System.out.println("Executing MyThread's custom run logic");
 // 如果不调用 super.run() 或 runnable.run(),外部传入的 Runnable 将不会被执行
 }
}

结论:在子类中重写 run() 后,由于多态性,JVM 会优先调用子类的 run() 实现。除非在子类 run() 中显式调用父类逻辑或执行传入的 Target,否则 Runnable 的任务体将被忽略。因此,推荐直接使用标准 new Thread(runnable) 方式以避免逻辑混淆。

完成判断与安全退出:如何终止线程

实际业务中,经常需要在特定条件下停止正在运行的线程。Java 官方已废弃 stop() 方法,因为它可能导致数据不一致或死锁。最通用的做法是使用标志位。

第1步:使用 volatile 布尔标志位

目的:通过外部信号通知线程自行退出循环或任务,实现平滑关闭。

class SafeStopRunnable implements Runnable {
 private volatile boolean stop = false; // 必须使用 volatile 保证可见性

 @Override
 public void run() {
 while (!stop) {
  // 执行任务逻辑
  try { Thread.sleep(100); } catch (InterruptedException e) { break; }
  System.out.println("Working...");
 }
 }

 public void shutdown() {
 this.stop = true;
 }
}

关键参数说明:volatile 修饰符确保当一个线程修改了 stop 变量,其他线程能立即读到最新值,避免缓存一致性问题。配合检查点(如循环条件、休眠结束处),可实现快速响应。

第2步:特殊状态下的中断处理

当线程处于阻塞状态(如调用 sleep、wait)时,外部设置标志位可能无法立即生效。此时应结合 Thread.interrupt() 方法。被中断的线程会抛出 InterruptedException,在 catch 块中清理资源并退出,从而确保阻塞中的线程也能及时响应终止指令。

简要总结

以上就是 Java 多线程详解:状态生命周期、创建方式与终止机制 的详细内容,更多关于 Java 并发编程基础与最佳实践的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。