Java 多线程详解:状态生命周期、创建方式与终止机制
深入理解 Java 多线程需掌握其生命周期状态转换与底层创建机制。本文将图解线程的五种基本状态,详细对比 Thread、Runnable 和 Callable 三种创建方式的多态表现与返回值处理,并提供基于布尔标志位的线程安全终止方案。
结果或目标:掌握多线程核心机制
多线程是 Java 并发编程的基石。理解线程的生命周期与正确的启动、销毁方式,有助于写出高效且安全的并发代码。本文将围绕线程状态的流转、不同创建方式的源码逻辑以及线程的安全退出三个维度展开。
准备条件:线程的五种基本状态
Java 线程在其生命周期中主要经历五种状态,这些状态构成了多线程调度的基础。
第1步:识别线程状态定义与图示
目的:建立对线程生命周期全貌的直观认知。
第2步:理解各状态的转换条件
目的:明确何时进入特定状态以及状态间的互斥关系。
- 新建状态 (New):通过
new关键字创建线程对象时进入。例如:Thread t = new MyThread();。此时仅分配了内存,尚未启动。 - 就绪状态 (Runnable):调用线程对象的
start()方法后进入。此时线程已获取除 CPU 以外的所有资源,等待调度。注意:start()不保证立即执行,仅表示具备执行资格。 - 运行状态 (Running):CPU 调度就绪队列中的线程时进入。这是唯一能真正执行代码的状态。关键点:就绪状态是进入运行状态的唯一入口,不存在从新建直接到运行的情况。
- 阻塞状态 (Blocked):运行中的线程因特定原因放弃 CPU 使用权而暂停。阻塞分为三类:
- 等待阻塞:调用
wait()方法。 - 同步阻塞:尝试获取
synchronized锁失败(锁被占用)。 - 其他阻塞:调用
sleep()、join()或发出 I/O 请求。超时或条件满足后重新转入就绪状态。
- 等待阻塞:调用
- 死亡状态 (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 并发编程基础与最佳实践的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













