🌺The Begin🌺点点关注,收藏不迷路🌺

1. 引言:线程也有“生老病死”

在 Java 多线程编程中,我们经常创建线程、启动线程,但很少思考:线程从创建到销毁,究竟经历了哪些状态?状态之间又是如何转换的?

理解线程的生命周期,就像理解人的生命阶段一样重要。只有掌握了线程在不同状态下的行为,才能写出正确、高效的并发程序。

本文将用流程图、源码分析和实战代码,全面剖析 Java 线程的六大状态及其转换规则。

2. 一图看懂:线程生命周期全景图

在这里插入图片描述

3. 六大状态的官方定义

Java 线程的六种状态定义在 java.lang.Thread.State 枚举中:

public enum State {
    NEW,           // 新建,尚未启动
    RUNNABLE,      // 可运行(就绪或运行中)
    BLOCKED,       // 阻塞(等待锁)
    WAITING,       // 无限等待
    TIMED_WAITING, // 限期等待
    TERMINATED     // 已终止
}

注意:Java 将操作系统层面的“就绪”和“运行”统一为 RUNNABLE 状态。

Java线程状态

RUNNABLE

BLOCKED/WAITING

OS线程状态

就绪 Ready

运行 Running

阻塞 Blocked

4. 逐个解析:六大状态详解

4.1 NEW:新生状态

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

Thread thread = new Thread(() -> System.out.println("Hello"));
System.out.println(thread.getState());  // NEW

特征

  • 只是一个普通的 Java 对象
  • 尚未与操作系统线程关联
  • 不能执行任何代码

4.2 RUNNABLE:就绪/运行状态

调用 start() 后进入此状态,表示线程可以运行或正在运行。

Thread thread = new Thread(() -> {
    System.out.println("Running...");
});
thread.start();
System.out.println(thread.getState());  // RUNNABLE

特征

  • 可能正在执行,也可能在等待 CPU 时间片
  • Java 不区分“就绪”和“运行”
  • 这是线程的正常工作状态

4.3 BLOCKED:阻塞状态

线程在等待获取锁(synchronized)时进入此状态。

public class BlockedDemo {
    private static final Object lock = new Object();
    
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                while (true) {}  // 死循环持有锁
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (lock) {  // 等待t1释放锁
                System.out.println("Will not reach here");
            }
        });
        
        t1.start();
        Thread.sleep(100);  // 确保t1先获得锁
        t2.start();
        Thread.sleep(100);
        
        System.out.println("t1 state: " + t1.getState());  // RUNNABLE
        System.out.println("t2 state: " + t2.getState());  // BLOCKED
    }
}

特征

  • 等待 synchronized
  • 不会响应中断(除非使用 Lock
  • 锁释放后自动进入 RUNNABLE

4.4 WAITING:无限等待状态

线程等待其他线程执行特定操作(通知),进入无限等待。

在这里插入图片描述

public class WaitingDemo {
    private static final Object lock = new Object();
    
    public static void main(String[] args) throws InterruptedException {
        Thread waiter = new Thread(() -> {
            synchronized (lock) {
                try {
                    lock.wait();  // 进入 WAITING 状态
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        waiter.start();
        Thread.sleep(100);  // 确保wait已执行
        
        System.out.println(waiter.getState());  // WAITING
    }
}

触发 WAITING 的方法

方法 等待条件 唤醒方式
Object.wait() 等待 notify/notifyAll notify()/notifyAll()
Thread.join() 等待目标线程死亡 目标线程结束
LockSupport.park() 等待 unpark unpark()

4.5 TIMED_WAITING:限期等待状态

与 WAITING 类似,但有等待时间限制。

public class TimedWaitingDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread timedWaiter = new Thread(() -> {
            try {
                Thread.sleep(5000);  // sleep 5秒,进入 TIMED_WAITING
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        
        timedWaiter.start();
        Thread.sleep(100);
        
        System.out.println(timedWaiter.getState());  // TIMED_WAITING
    }
}

触发 TIMED_WAITING 的方法

方法 说明
Thread.sleep(millis) 休眠指定时间
wait(timeout) 等待指定时间或通知
join(millis) 等待目标线程指定时间
parkNanos(nanos) 限期等待 unpark
parkUntil(deadline) 等待到绝对时间点

4.6 TERMINATED:终止状态

线程执行完毕或异常退出后的状态。

Thread thread = new Thread(() -> System.out.println("Done"));
thread.start();
thread.join();  // 等待执行完成
System.out.println(thread.getState());  // TERMINATED

特征

  • 线程生命周期结束
  • 不能再次调用 start()
  • 线程对象仍然存在,可以查询状态

5. 状态转换的完整流程

渲染错误: Mermaid 渲染失败: Parse error on line 3: ... N[NEW] -->|start()| R[RUNNABLE] -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

6. 深入源码:状态追踪

6.1 Thread 类中的状态相关方法

public class Thread {
    // 获取当前线程状态
    public State getState() {
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }
    
    // 判断线程是否存活(不是 NEW 和 TERMINATED)
    public final native boolean isAlive();
    
    // 等待线程终止
    public final void join() throws InterruptedException {
        join(0);  // join(0) 表示无限等待
    }
    
    // 让出 CPU(从 Running -> Runnable)
    public static native void yield();
}

6.2 实现一个状态监控器

public class ThreadStateMonitor {
    private final Thread thread;
    private volatile State lastState;
    
    public ThreadStateMonitor(Thread thread) {
        this.thread = thread;
        this.lastState = thread.getState();
    }
    
    public void startMonitoring() {
        Thread monitor = new Thread(() -> {
            while (thread.getState() != State.TERMINATED) {
                State currentState = thread.getState();
                if (currentState != lastState) {
                    System.out.printf("[%s] %s -> %s%n", 
                        thread.getName(), lastState, currentState);
                    lastState = currentState;
                }
                try {
                    Thread.sleep(10);  // 每10ms检查一次
                } catch (InterruptedException e) {
                    break;
                }
            }
            System.out.printf("[%s] TERMINATED%n", thread.getName());
        });
        monitor.setDaemon(true);
        monitor.start();
    }
    
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(500);      // TIMED_WAITING
                synchronized (ThreadStateMonitor.class) {
                    Thread.sleep(500);  // RUNNABLE
                }
            } catch (InterruptedException e) { }
        }, "Worker");
        
        ThreadStateMonitor monitor = new ThreadStateMonitor(t);
        monitor.startMonitoring();
        t.start();
        t.join();
    }
}

7. 常见误区与对比

7.1 误区一:RUNNABLE = 正在运行?

// 这个线程在长时间 I/O 时,状态仍然是 RUNNABLE
Thread ioThread = new Thread(() -> {
    try {
        // 从慢速网络读取数据
        URL url = new URL("http://very-slow-server.com");
        InputStream in = url.openStream();
        int data = in.read();  // 阻塞等待数据
    } catch (IOException e) { }
});

解释:Java 层面的 I/O 阻塞不改变线程状态,JVM 认为线程仍在等待 CPU,只是操作系统将其挂起。

7.2 误区二:BLOCKED vs WAITING

方面 BLOCKED WAITING
等待对象 synchronized 锁 其他线程的通知
是否可中断 是(interrupt 可唤醒)
典型场景 锁竞争 wait/join/park
唤醒方式 锁释放 notify/unpark/线程结束

7.3 误区三:yield 会改变状态?

Thread.yield();  // 不会改变线程状态

yield() 只是提示调度器:当前线程愿意让出 CPU。线程仍然处于 RUNNABLE 状态,只是从“运行”变为“就绪”。

8. 实战:线程状态的可视化

8.1 创建各种状态的线程

public class ThreadStateDemo {
    
    public static void main(String[] args) throws InterruptedException {
        // NEW 状态
        Thread newThread = new Thread(() -> {});
        System.out.println("1. NEW: " + newThread.getState());
        
        // RUNNABLE 状态
        Thread runnableThread = new Thread(() -> {
            while (true) {
                // 死循环,保持运行
            }
        });
        runnableThread.start();
        System.out.println("2. RUNNABLE: " + runnableThread.getState());
        
        // BLOCKED 状态
        Object lock = new Object();
        Thread holder = new Thread(() -> {
            synchronized (lock) {
                try { Thread.sleep(10000); } catch (InterruptedException e) {}
            }
        });
        Thread blockedThread = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Got lock");
            }
        });
        holder.start();
        Thread.sleep(100);
        blockedThread.start();
        Thread.sleep(100);
        System.out.println("3. BLOCKED: " + blockedThread.getState());
        
        // WAITING 状态
        Thread waitingThread = new Thread(() -> {
            synchronized (lock) {
                try { lock.wait(); } catch (InterruptedException e) {}
            }
        });
        waitingThread.start();
        Thread.sleep(100);
        System.out.println("4. WAITING: " + waitingThread.getState());
        
        // TIMED_WAITING 状态
        Thread timedThread = new Thread(() -> {
            try { Thread.sleep(10000); } catch (InterruptedException e) {}
        });
        timedThread.start();
        Thread.sleep(100);
        System.out.println("5. TIMED_WAITING: " + timedThread.getState());
        
        // TERMINATED 状态
        Thread terminatedThread = new Thread(() -> {});
        terminatedThread.start();
        terminatedThread.join();
        System.out.println("6. TERMINATED: " + terminatedThread.getState());
    }
}

8.2 输出结果

1. NEW: NEW
2. RUNNABLE: RUNNABLE
3. BLOCKED: BLOCKED
4. WAITING: WAITING
5. TIMED_WAITING: TIMED_WAITING
6. TERMINATED: TERMINATED

9. 状态转换常见问题排查

9.1 线程为什么一直处于 RUNNABLE?

// 可能原因1:死循环
while(true) { }

// 可能原因2:高 CPU 计算
BigInteger.probablePrime(2048, new Random());

// 可能原因3:JNI 调用卡住
nativeMethod();  // 状态不会改变

// 可能原因4:网络 I/O(底层 socket 读取)
socket.getInputStream().read();  // 仍是 RUNNABLE

9.2 线程为什么一直处于 WAITING?

// 可能原因1:object.wait() 后无人 notify
synchronized(obj) {
    obj.wait();  // 永远等待
}

// 可能原因2:thread.join() 的目标线程无限循环
Thread target = new Thread(() -> { while(true); });
target.start();
target.join();  // 永远等待

// 可能原因3:线程池的等待队列
ExecutorService pool = Executors.newFixedThreadPool(1);
Future<?> future = pool.submit(() -> { while(true); });
future.get();  // 永远等待

10. 总结与速查表

10.1 线程状态速查表

状态 英文 如何进入 如何离开
新建 NEW new Thread() start()
可运行 RUNNABLE start() 等待 CPU/锁/通知
阻塞 BLOCKED 等待 synchronized 获得锁
无限等待 WAITING wait()/join()/park() notify()/线程结束/unpark()
限期等待 TIMED_WAITING sleep(t)/wait(t)/join(t)/parkNanos() 超时/通知
终止 TERMINATED run() 结束 无法离开

10.2 关键要点

线程生命周期要点

RUNNABLE包含就绪和运行

I/O阻塞不影响Java状态

BLOCKED vs WAITING

BLOCKED等锁

WAITING等通知

不能重复start

线程终止后只能查询状态

状态转换不可逆

NEW → RUNNABLE → TERMINATED

RUNNABLE可进入其他状态再回来

一句话总结:

Java 线程有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 六种状态。理解这些状态及其转换规则,是编写正确并发程序的基础。其中 RUNNABLE 包含了操作系统层面的“就绪”和“运行”,而 synchronized 锁等待导致 BLOCKED,wait()/join()/park() 导致 WAITING,带有超时参数的方法导致 TIMED_WAITING。


📌 调试技巧:使用 jstack <pid>Thread.getAllStackTraces() 可以查看所有线程的状态,是排查死锁、线程卡死问题的利器。

如果觉得本文对你有帮助,欢迎点赞、收藏、转发~

在这里插入图片描述


🌺The End🌺点点关注,收藏不迷路🌺
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐