杰瑞科技汇

Java线程状态有哪些?

线程状态概览

在 Java 中,线程的状态由 java.lang.Thread.State 枚举类定义,根据 Java 官方文档和 Java 内存模型,一个线程在任何时刻都只可能处于以下六种状态之一:

Java线程状态有哪些?-图1
(图片来源网络,侵删)
  1. NEW (新建)
  2. RUNNABLE (可运行)
  3. BLOCKED (阻塞)
  4. WAITING (等待)
  5. TIMED_WAITING (定时等待)
  6. TERMINATED (终止)

这六种状态构成了线程完整的生命周期,下面我们逐一解析每个状态的含义、如何进入该状态,以及如何从该状态转换出去。


NEW (新建状态)

  • 描述:线程被创建,但尚未启动,线程只是一个对象,JVM 没有为其分配系统资源(如调用栈空间)。
  • 如何进入:当你使用 new 关键字创建一个 Thread 实例时,它就处于 NEW 状态。
  • 如何转换
    • 调用线程对象的 start() 方法,状态将变为 RUNNABLE
    • 如果不调用 start(),线程将一直保持 NEW 状态。
  • 特点:在这个状态下,调用 run() 方法是无效的,它不会开启新线程,而是直接在当前线程中执行 run() 方法的代码。

示例代码:

Thread myThread = new Thread(() -> {
    System.out.println("Thread is running.");
});
System.out.println(myThread.getState()); // 输出: NEW
myThread.start(); // 启动线程

RUNNABLE (可运行状态)

  • 描述:这是最复杂也最常用的状态,它表示线程已经准备好,并且正在运行中,或者正在等待操作系统分配 CPU 时间片,在 Java 6 之前,这个状态被称为 "Running",但为了更准确地反映现代操作线程调度机制(线程可能就绪但尚未真正运行),Java 6 将其更名为 "Runnable"。
  • 如何进入
    • NEW 状态调用 start() 方法。
    • 线程从 BLOCKEDWAITINGTIMED_WAITING 状态转换回来。
  • 如何转换
    • 当线程执行完 run() 方法后,状态变为 TERMINATED
    • 当线程需要获取锁但未能获取时,状态变为 BLOCKED
    • 当线程主动调用 wait(), join(), LockSupport.park() 等方法时,状态变为 WAITINGTIMED_WAITING
  • 特点RUNNABLE 状态涵盖了操作系统层面的 "就绪" 和 "运行" 两种情况,对于 Java 我们无法直接区分线程是正在运行还是在等待 CPU,这个转换由操作系统和 JVM 调度器完成。

示例代码:

Thread myThread = new Thread(() -> {
    System.out.println("Thread is running. State: " + Thread.currentThread().getState());
});
myThread.start();
// 主线程稍后查看状态
try {
    Thread.sleep(10); // 给新线程一点时间启动
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println(myThread.getState()); // 输出: RUNNABLE (或 TERMINMINATED,如果执行太快)

BLOCKED (阻塞状态)

  • 描述:线程因为等待一个监视器锁(monitor lock)而处于阻塞状态,换句话说,当一个线程试图进入一个被 synchronized 修饰的代码块或方法时,如果锁正被其他线程持有,那么该线程就会进入 BLOCKED 状态。
  • 如何进入:当线程尝试获取一个已经被其他线程占有的 synchronized 锁时。
  • 如何转换
    • 当其他线程释放了该锁,JVM 选择当前线程作为下一个获取锁的线程时,状态将变为 RUNNABLE
  • 特点BLOCKED 状态是线程间竞争资源(锁)的直接体现,线程在 BLOCKED 状态时,不消耗 CPU 资源,只是被动等待。

示例代码:

Java线程状态有哪些?-图2
(图片来源网络,侵删)
final Object lock = new Object();
// 线程A
Thread threadA = new Thread(() -> {
    synchronized (lock) {
        System.out.println("Thread A acquired the lock.");
        try {
            Thread.sleep(100); // 持锁一段时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread A released the lock.");
    }
});
// 线程B
Thread threadB = new Thread(() -> {
    System.out.println("Thread B is trying to acquire the lock...");
    synchronized (lock) {
        System.out.println("Thread B acquired the lock.");
    }
});
threadA.start();
threadB.start();
try {
    Thread.sleep(50); // 让线程A先拿到锁
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("Thread B's state: " + threadB.getState()); // 输出: BLOCKED

WAITING (等待状态) 和 5. TIMED_WAITING (定时等待状态)

这两种状态都属于等待状态,但它们的唤醒方式不同。

WAITING (无限等待状态)

  • 描述:线程等待另一个线程来执行一个特定的操作,这种等待是无限的,直到被其他线程唤醒。
  • 如何进入:线程主动调用以下方法之一:
    • Object.wait() (无参数)
    • Thread.join() (无参数)
    • LockSupport.park()
  • 如何转换
    • 被其他线程调用 Object.notify()Object.notifyAll() 唤醒后,状态变为 RUNNABLE(但需要重新竞争锁)。
    • Thread.interrupt() 中断后,会抛出 InterruptedException,并退出等待状态。
  • 特点:进入 WAITING 状态的线程会释放其持有的锁(如果是通过 wait() 进入的)。

TIMED_WAITING (定时等待状态)

  • 描述:与 WAITING 类似,但它是在一个指定的时间段内等待,超时后,线程会自动被唤醒。
  • 如何进入:线程主动调用带有超时参数的方法之一:
    • Thread.sleep(long milliseconds)
    • Object.wait(long timeout)
    • Thread.join(long millis)
    • LockSupport.parkNanos(long nanos)
    • LockSupport.parkUntil(long deadline)
  • 如何转换
    • 指定的等待时间到期,状态变为 RUNNABLE
    • 在等待期间被其他线程唤醒(notify() / notifyAll()),状态变为 RUNNABLE
    • 在等待期间被 Thread.interrupt() 中断,抛出 InterruptedException,并退出等待状态。
  • 特点sleep() 是一个静态方法,它让当前线程休眠,并且不会释放任何锁,而 wait() 会释放锁。

示例代码:

final Object lock = new Object();
Thread waitingThread = new Thread(() -> {
    synchronized (lock) {
        try {
            System.out.println("Waiting thread is waiting...");
            // 进入 TIMED_WAITING 状态
            lock.wait(2000); 
            System.out.println("Waiting thread woke up.");
        } catch (InterruptedException e) {
            System.out.println("Waiting thread was interrupted.");
        }
    }
});
waitingThread.start();
try {
    Thread.sleep(1000); // 主线程等待1秒
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("Waiting thread state after 1s: " + waitingThread.getState()); // 输出: TIMED_WAITING
// 唤醒等待的线程
synchronized (lock) {
    lock.notify(); // 或者不调用,让它自动超时
}
try {
    waitingThread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("Waiting thread state at the end: " + waitingThread.getState()); // 输出: TERMINATED

TERMINATED (终止状态)

  • 描述:线程的 run() 方法已经执行完毕,或者因为未捕获的异常而终止,线程的生命周期结束。
  • 如何进入
    • 线程的 run() 方法正常执行完毕。
    • 线程的 run() 方法抛出了一个未捕获的异常。
  • 如何转换:一旦进入 TERMINATED 状态,线程就不可再被启动,调用 start() 方法会抛出 IllegalStateException
  • 特点TERMINATED 状态的线程对象仍然可以被引用,但其内部的生命周期已经结束。

状态转换图

为了更直观地理解,我们可以用一个状态转换图来表示:

graph TD
    A[NEW] -->|start()| B[RUNNABLE];
    B -->|run()方法结束| F[TERMINATED];
    B -->|获取synchronized锁失败| C[BLOCKED];
    C -->|成功获取锁| B;
    B -->|调用wait(), join(), park()| D[WAITING];
    B -->|调用sleep(), wait(timeout), join(timeout)| E[TIMED_WAITING];
    D -->|被notify()/notifyAll()中断| B;
    E -->|超时/被唤醒| B;
    D -->|被interrupt()中断| B;
    E -->|被interrupt()中断| B;
    C -->|被interrupt()中断| B; // 注意:中断一个BLOCKED的线程会将其状态变为RUNNABLE,并抛出InterruptedException

总结与关键点

  1. RUNNABLE 是核心:它涵盖了“就绪”和“运行”,是线程工作的主要状态。
  2. BLOCKED vs WAITING
    • BLOCKED被动的,因为竞争锁而无法运行。
    • WAITING / TIMED_WAITING主动的,因为调用了 wait(), sleep() 等方法而暂停。
  3. 锁的释放
    • BLOCKED 状态下,线程不持有锁,只是在等待锁。
    • WAITING 状态下(由 wait() 导致),线程会释放其持有的锁
    • TIMED_WAITING 状态下(由 sleep() 导致),线程不会释放任何锁
  4. 查看状态:可以通过 Thread.getState() 方法在任何时候获取线程的当前状态,这对于调试非常有用。

掌握这些状态及其转换,是进行高效、健壮的多线程编程的必备知识。

Java线程状态有哪些?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇