Java线程状态详解:从创建到终止的完整生命周期

在Java并发编程中,理解线程的状态及其转换机制是掌握多线程编程的基础。线程作为程序执行的最小单元,其状态变化直接反映了程序的运行情况。本文将详细解析Java线程的6种状态,以及状态之间的转换规则,帮助你深入理解线程的生命周期。

一、Java线程状态的定义

Java中线程的状态被定义在java.lang.Thread.State枚举类中,共包含6种状态:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED
}

这些状态是JVM层面的定义,与操作系统底层的线程状态(如就绪、运行、阻塞等)并不完全一一对应,但反映了线程在JVM中的生命周期阶段。我们可以通过Thread.getState()方法获取当前线程的状态。

二、6种线程状态详解

1. NEW(新建状态)

定义:当线程对象被创建但尚未调用start()方法时,线程处于NEW状态。

特点

  • 线程尚未启动,没有分配底层操作系统资源
  • 此时线程仅作为一个Java对象存在于堆内存中
  • 可以调用start()方法启动,或调用stop()(已废弃)终止

示例

Thread thread = new Thread(() -> {
    // 线程执行逻辑
});
System.out.println(thread.getState()); // 输出:NEW

2. RUNNABLE(可运行状态)

定义:调用start()方法后,线程进入RUNNABLE状态。该状态包含两种情况:

  • 线程正在CPU上执行(运行中)
  • 线程处于就绪状态,等待CPU调度(未运行但可立即执行)

特点

  • 线程已获取必要的资源(如栈空间),并被纳入线程调度器管理
  • 处于此状态的线程可能正在执行,也可能在等待CPU时间片
  • 这是Java对"就绪"和"运行"两种操作系统状态的统一抽象

触发条件

  • 新建线程调用start()方法后进入RUNNABLE状态
  • 其他状态(如BLOCKED、WAITING等)的线程恢复后进入RUNNABLE状态

注意start()方法只能调用一次,多次调用会抛出IllegalThreadStateException

3. BLOCKED(阻塞状态)

定义:线程因等待获取监视器锁(synchronized锁)而处于阻塞状态。

特点

  • 仅与同步机制(synchronized关键字)相关,是一种"主动等待"锁的状态
  • 当线程试图进入synchronized修饰的方法或代码块时,若锁已被其他线程持有,则当前线程进入BLOCKED状态
  • 一旦持有锁的线程释放锁,JVM会从BLOCKED状态的线程中选择一个唤醒,使其进入RUNNABLE状态

示例场景
阻塞状态

// 共享锁对象
Object lock = new Object();

// 线程1先获取锁
Thread thread1 = new Thread(() -> {
    synchronized (lock) {
        try {
            Thread.sleep(1000); // 持有锁休眠
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

// 线程2后获取锁,会进入BLOCKED状态
Thread thread2 = new Thread(() -> {
    synchronized (lock) { // 此时锁被thread1持有
        System.out.println("Thread2获得锁");
    }
});

thread1.start();
Thread.sleep(100); // 确保thread1先启动
thread2.start();
Thread.sleep(100);
System.out.println(thread2.getState()); // 输出:BLOCKED

4. WAITING(无限期等待状态)

定义:线程进入一种无时间限制的等待状态,需要其他线程显式唤醒才能继续执行。

特点

  • 处于WAITING状态的线程不会被分配CPU时间片
  • 必须由其他线程执行特定操作(如调用notify())才能唤醒
  • 此状态的线程已释放持有的监视器锁(若有)

进入WAITING状态的方法

  • Object.wait():当前线程释放锁并进入WAITING状态,需被其他线程notify()notifyAll()唤醒
  • Thread.join():等待目标线程执行完毕,需目标线程终止才能唤醒
  • LockSupport.park():无参数版本,需其他线程调用LockSupport.unpark(Thread)唤醒

示例
无限等待

Object lock = new Object();
Thread thread = new Thread(() -> {
    synchronized (lock) {
        try {
            lock.wait(); // 释放锁并进入WAITING状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // 输出:WAITING

5. TIMED_WAITING(限期等待状态)

定义:线程进入一种有时间限制的等待状态,无需其他线程唤醒,等待时间结束后会自动恢复;也可被提前唤醒。

特点

  • 与WAITING状态类似,但有超时机制
  • 超时时间到达后会自动转换为RUNNABLE状态
  • 也可被其他线程提前唤醒(如notify()或中断)

进入TIMED_WAITING状态的方法

  • Thread.sleep(long millis):线程休眠指定时间,不释放监视器锁
  • Object.wait(long timeout):释放锁并等待指定时间,超时后自动唤醒
  • Thread.join(long millis):等待目标线程指定时间
  • LockSupport.parkNanos(long nanos):限时暂停当前线程
  • LockSupport.parkUntil(long deadline):暂停至指定时间点

示例
有限等待

Thread thread = new Thread(() -> {
    try {
        Thread.sleep(1000); // 进入TIMED_WAITING状态
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // 输出:TIMED_WAITING

注意Thread.sleep()Object.wait()的核心区别:

  • sleep()Thread类的静态方法,wait()Object类的实例方法
  • sleep()不释放监视器锁,wait()会释放监视器锁
  • sleep()时间到后自动唤醒,wait()需显式唤醒或超时唤醒

6. TERMINATED(终止状态)

定义:线程执行完毕(run()方法正常返回)或因异常终止后,进入TERMINATED状态。

特点

  • 线程的生命周期已结束,无法再回到其他状态
  • 线程对象仍然存在于堆中,但已不具备执行能力
  • 可以调用isAlive()方法判断线程是否已终止(返回false

示例

Thread thread = new Thread(() -> {
    // 空实现,立即执行完毕
});

thread.start();
Thread.sleep(100); // 等待线程执行完毕
System.out.println(thread.getState()); // 输出:TERMINATED
System.out.println(thread.isAlive()); // 输出:false

三、线程状态转换全解析

线程状态之间的转换遵循特定规则,下图展示了完整的状态转换路径:
完整转换图

NEW → RUNNABLE → TERMINATED
       ↑    ↓
       ├→ BLOCKED → RUNNABLE
       │
       ├→ WAITING → RUNNABLE
       │      ↑      ↓
       │      └──────┘
       │
       └→ TIMED_WAITING → RUNNABLE
              ↑            ↓
              └────────────┘

详细转换规则:

  1. NEW → RUNNABLE
    唯一途径:调用start()方法。JVM会为线程分配底层资源,并将其加入调度队列。

  2. RUNNABLE → BLOCKED
    当线程试图获取synchronized锁但该锁被其他线程持有时,进入BLOCKED状态。

  3. BLOCKED → RUNNABLE
    当持有synchronized锁的线程释放锁,且当前线程获得该锁时,从BLOCKED转换为RUNNABLE。

  4. RUNNABLE → WAITING
    线程执行以下操作时进入WAITING状态:

    • 持有锁时调用Object.wait()
    • 调用其他线程的join()(无参)
    • 调用LockSupport.park()
  5. WAITING → RUNNABLE
    唤醒条件:

    • 其他线程调用Object.notify()notifyAll(),且当前线程重新获得锁
    • join()的线程执行完毕
    • 其他线程调用LockSupport.unpark(Thread)
  6. RUNNABLE → TIMED_WAITING
    线程执行以下限时等待操作时进入该状态:

    • Thread.sleep(long)
    • 持有锁时调用Object.wait(long)
    • 调用其他线程的join(long)
    • LockSupport.parkNanos(long)parkUntil(long)
  7. TIMED_WAITING → RUNNABLE
    唤醒条件:

    • 等待时间超时
    • 其他线程提前唤醒(如notify()unpark()等)
    • 线程被中断(interrupt()
  8. RUNNABLE → TERMINATED
    线程的run()方法执行完毕,或因未捕获的异常终止。

  9. 其他状态 → TERMINATED
    线程在任何状态下被强制终止(如调用已废弃的stop()方法),但不推荐使用这种方式,可能导致资源泄露。

特殊情况:中断(interrupt)对状态的影响

线程的interrupt()方法可以中断处于WAITING或TIMED_WAITING状态的线程,使其抛出InterruptedException并转换为RUNNABLE状态:

Thread thread = new Thread(() -> {
    try {
        Thread.sleep(10000); // 进入TIMED_WAITING
    } catch (InterruptedException e) {
        System.out.println("线程被中断");
    }
});

thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // TIMED_WAITING

thread.interrupt(); // 中断线程
Thread.sleep(100);
System.out.println(thread.getState()); // RUNNABLE(若已处理异常)

四、常见状态转换场景示例

场景1:同步代码块导致的BLOCKED转换

Object lock = new Object();

// 线程A获取锁后休眠
Thread threadA = new Thread(() -> {
    synchronized (lock) {
        try {
            Thread.sleep(2000); // 持有锁休眠(TIMED_WAITING)
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

// 线程B尝试获取同一把锁
Thread threadB = new Thread(() -> {
    synchronized (lock) { // 此时锁被A持有,进入BLOCKED
        System.out.println("线程B获得锁");
    }
});

threadA.start();
Thread.sleep(100); // 确保A先启动
threadB.start();
Thread.sleep(100);

System.out.println("threadA状态:" + threadA.getState()); // TIMED_WAITING
System.out.println("threadB状态:" + threadB.getState()); // BLOCKED

// 2秒后,threadA释放锁,threadB获得锁进入RUNNABLE
Thread.sleep(2000);
System.out.println("threadB状态:" + threadB.getState()); // RUNNABLE(或已TERMINATED)

场景2:wait()与notify()的状态转换

Object lock = new Object();
Thread waiter = new Thread(() -> {
    synchronized (lock) {
        try {
            System.out.println("等待唤醒...");
            lock.wait(); // 释放锁,进入WAITING
            System.out.println("被唤醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

Thread notifier = new Thread(() -> {
    synchronized (lock) {
        try {
            Thread.sleep(1000); // 等待waiter进入WAITING
            lock.notify(); // 唤醒waiter
            System.out.println("已发送唤醒通知");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

waiter.start();
Thread.sleep(100);
System.out.println("waiter状态:" + waiter.getState()); // WAITING

notifier.start();
Thread.sleep(1000);
System.out.println("waiter状态:" + waiter.getState()); // RUNNABLE(重新竞争锁后)

五、总结

Java线程的6种状态构成了一个完整的生命周期,从新建(NEW)到启动(RUNNABLE),经过各种等待状态(BLOCKED、WAITING、TIMED_WAITING),最终到终止(TERMINATED)。理解这些状态及其转换规则,对于诊断并发问题、优化多线程程序至关重要。

关键要点:

  • BLOCKED仅与synchronized锁竞争相关
  • WAITING和TIMED_WAITING的区别在于是否有超时机制
  • 线程状态转换反映了线程对资源(CPU、锁)的获取与释放过程
  • 避免使用已废弃的线程控制方法(如stop()suspend()),应通过中断机制或等待/通知机制安全控制线程

掌握线程状态转换,能帮助你写出更健壮的并发程序,有效解决死锁、活锁等常见并发问题。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐