java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java线程生命周期

Java 线程生命周期与状态转换的核心路径

作者:C雨后彩虹

本文详细讲解了Java线程的6种状态,通过4个代码案例演示了状态转换的核心路径,并介绍了使用 jstack 命令分析线程状态的实战方法,理解线程的生命周期和状态转换,是后续学习线程同步、线程通信的基础,感兴趣的朋友跟随小编一起看看吧

一、前言

在上一篇文章中,我们学习了 Java 中创建线程的 5 种方式,掌握了线程的基础创建与启动逻辑。但线程从创建到销毁并非一成不变,而是会在不同状态之间转换。理解线程的生命周期和状态转换,是解决多线程并发问题的关键。

本文将从理论到实践,深入解析 Java 线程的 6 种状态,演示状态转换的核心路径,并通过工具分析线程状态,帮助大家彻底掌握这一核心知识点。

二、Java 线程的 6 种状态

Java 线程的状态被明确枚举在 Thread.State 中,共包含 6 种状态 ,任何时刻一个线程只能处于其中一种状态。

状态名称

核心含义

进入条件

退出条件

NEW

(新建)

线程对象已创建,但未调用start()方法

创建 Thread 实例后,未执行 start()

调用 start() 方法

RUNNABLE

(可运行)

线程正在 CPU 上执行,或处于就绪状态等待 CPU 调度

调用 start() 方法;从其他阻塞 / 等待状态唤醒

线程执行完毕;进入阻塞 / 等待状态

BLOCKED

(阻塞)

线程因竞争锁失败而被阻塞

线程尝试进入 synchronized 代码块 / 方法,但锁被其他线程持有

成功获取到锁

WAITING

(等待)

线程无时间限制等待,需其他线程主动唤醒

调用 Object.wait()(无参)、Thread.join()(无参)、LockSupport.park()

其他线程调用 Object.notify()/notifyAll()、被 join 的线程执行完毕、调用 LockSupport.unpark()

TIMED_WAITING

(超时等待)

线程有时间限制等待,超时后自动唤醒

调用 Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()/parkUntil()

超时自动唤醒;其他线程提前唤醒

TERMINATED

(终止)

线程执行完毕或异常退出

run() 方法执行完成;线程执行过程中抛出未捕获异常

无(线程生命周期结束)

三、线程状态转换流程图

四、线程状态转换

线程状态转换有固定的核心路径,我们通过代码案例逐一演示关键转换场景,直观观察状态变化。

前置说明

场景 1:NEW → RUNNABLE → TERMINATED(基础路径)

核心逻辑 :线程创建后处于 NEW 状态,调用 start() 进入 RUNNABLE ,执行完 run() 后进入 TERMINATED 。

public class ThreadStateDemo1 {
    public static void main(String[] args) throws InterruptedException {
        // 1. 创建线程,此时状态为 NEW
        Thread thread = new Thread(() -> {
            System.out.println("线程执行中,当前状态:" + Thread.currentThread().getState());
            // 模拟任务执行
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "基础状态线程");
        System.out.println("调用start()前,线程状态:" + thread.getState()); // NEW
        // 2. 启动线程,进入 RUNNABLE 状态
        thread.start();
        Thread.sleep(50); // 等待线程进入执行状态
        System.out.println("线程执行中,主线程观察到的状态:" + thread.getState()); // RUNNABLE
        // 3. 等待线程执行完毕
        thread.join();
        System.out.println("线程执行完毕,当前状态:" + thread.getState()); // TERMINATED
    }
}

运行结果

调用start()前,线程状态:NEW
线程执行中,当前状态:RUNNABLE
线程执行中,主线程观察到的状态:RUNNABLE
线程执行完毕,当前状态:TERMINATED

场景 2:RUNNABLE → BLOCKED → RUNNABLE(锁竞争导致阻塞)

核心逻辑 :线程尝试获取 synchronized 锁失败时,进入 BLOCKED 状态;成功获取锁后,回到 RUNNABLE 。

public class ThreadStateDemo2 {
    // 定义一个锁对象
    private static final Object LOCK = new Object();
    public static void main(String[] args) throws InterruptedException {
        // 线程1:先获取锁,持有锁1秒
        Thread thread1 = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 获取到锁,状态:RUNNABLE");
                    Thread.sleep(1000); // 持有锁,让线程2阻塞
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程1");
        // 线程2:后获取锁,会进入 BLOCKED 状态
        Thread thread2 = new Thread(() -> {
            synchronized (LOCK) {
                System.out.println(Thread.currentThread().getName() + " 成功获取锁,状态回到 RUNNABLE");
            }
        }, "线程2");
        // 启动线程1和线程2
        thread1.start();
        Thread.sleep(100); // 确保线程1先获取锁
        thread2.start();
        // 观察线程2的状态
        Thread.sleep(200);
        System.out.println(thread2.getName() + " 等待锁,状态:" + thread2.getState()); // BLOCKED
        // 等待线程1执行完毕,线程2获取锁
        thread1.join();
        Thread.sleep(200);
        System.out.println(thread2.getName() + " 执行中,状态:" + thread2.getState()); // RUNNABLE
    }
}

运行结果

线程1 获取到锁,状态:RUNNABLE
线程2 等待锁,状态:BLOCKED
线程2 成功获取锁,状态回到 RUNNABLE
线程2 执行中,状态:RUNNABLE

场景 3:RUNNABLE → WAITING → RUNNABLE(无参 wait () 唤醒)

核心逻辑 :线程调用 Object.wait() (无参)后,释放锁并进入 WAITING 状态;需其他线程调用 notify() / notifyAll() 唤醒,重新竞争锁后回到 RUNNABLE 。

public class ThreadStateDemo3 {
    private static final Object LOCK = new Object();
    private static Thread waitThread; // 等待线程
    public static void main(String[] args) throws InterruptedException {
        // 等待线程:调用wait()进入WAITING状态
        waitThread = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 调用wait(),进入WAITING状态");
                    LOCK.wait(); // 无参wait,释放锁
                    System.out.println(Thread.currentThread().getName() + " 被唤醒,状态回到RUNNABLE");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "等待线程");
        // 唤醒线程:调用notify()唤醒等待线程
        Thread notifyThread = new Thread(() -> {
            synchronized (LOCK) {
                System.out.println(Thread.currentThread().getName() + " 获取锁,准备唤醒等待线程");
                LOCK.notify(); // 唤醒等待线程
                try {
                    Thread.sleep(500); // 持有锁,让等待线程先处于WAITING→BLOCKED
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "唤醒线程");
        waitThread.start();
        Thread.sleep(200);
        System.out.println(waitThread.getName() + " 当前状态:" + waitThread.getState()); // WAITING
        notifyThread.start();
        notifyThread.join();
        Thread.sleep(200);
        System.out.println(waitThread.getName() + " 最终状态:" + waitThread.getState()); // RUNNABLE/TERMINATED
    }
}

运行结果

等待线程 调用wait(),进入WAITING状态
等待线程 当前状态:WAITING
唤醒线程 获取锁,准备唤醒等待线程
等待线程 被唤醒,状态回到RUNNABLE
等待线程 最终状态:TERMINATED

场景 4:RUNNABLE → TIMED_WAITING → RUNNABLE(sleep () 超时唤醒)

核心逻辑 :线程调用 Thread.sleep(long) 后,进入 TIMED_WAITING 状态;超时后自动唤醒,回到 RUNNABLE 状态( sleep () 不会释放锁 )。

public class ThreadStateDemo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread sleepThread = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " 调用sleep(1000),进入TIMED_WAITING状态");
                Thread.sleep(1000); // 超时等待1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 超时唤醒,状态回到RUNNABLE");
        }, "睡眠线程");
        sleepThread.start();
        Thread.sleep(200);
        System.out.println(sleepThread.getName() + " 当前状态:" + sleepThread.getState()); // TIMED_WAITING
        sleepThread.join();
        System.out.println(sleepThread.getName() + " 执行完毕,状态:" + sleepThread.getState()); // TERMINATED
    }
}

运行结果

睡眠线程 调用sleep(1000),进入TIMED_WAITING状态
睡眠线程 当前状态:TIMED_WAITING
睡眠线程 超时唤醒,状态回到RUNNABLE
睡眠线程 执行完毕,状态:TERMINATED

五、使用 jstack 命令分析线程状态

在实际开发中,我们无法通过代码实时观察线上线程的状态,此时可以使用 JDK 自带的 jstack 命令,查看线程的状态和调用栈,定位线程阻塞、死锁等问题。

1、操作步骤

1. 获取进程 ID:使用 jps 命令,查看 Java 进程的 PID。

jps
# 输出示例:1234 ThreadStateDemo2 (1234为PID)

2. 导出线程快照:使用 jstack PID > thread_dump.txt 命令,将线程快照导出到文件。

jstack 1234 > thread_dump.txt

3. 分析线程状态:打开 thread_dump.txt 文件,搜索线程名称,查看线程状态。

2、实战案例(分析 BLOCKED 状态线程)

在 ThreadStateDemo2 运行时,导出线程快照,会看到类似如下内容:

"线程2" #13 prio=5 os_prio=31 tid=0x00007f8b1282e000 nid=0x5003 waiting for monitor entry [0x0000700001a6f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at ThreadStateDemo2.lambda$main$1(ThreadStateDemo2.java:18)
        - waiting to lock <0x000000076ab81810> (a java.lang.Object)
        - locked <0x000000076ab81810> (a java.lang.Object)

六、常见误区辨析

六、总结

本文详细讲解了 Java 线程的 6 种状态,通过 4 个代码案例演示了状态转换的核心路径,并介绍了使用 jstack 命令分析线程状态的实战方法。理解线程的生命周期和状态转换,是后续学习线程同步、线程通信的基础。

下一篇文章,我们将深入分析并发编程的三大核心特性(原子性、可见性、有序性),并讲解volatile关键字的原理与使用场景。

到此这篇关于Java 线程生命周期与状态转换的文章就介绍到这了,更多相关java线程生命周期内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文