JavaEE——多线程(3)

1.Java线程状态概述

Java 线程的生命周期由Thread.State枚举明确规定,共 6 种状态,分别是:

  1. 新建状态(New)
  2. 可运行状态(Runnable)
  3. 阻塞状态(Blocked)
  4. 等待状态(Waiting)
  5. 超时等待状态(Timed_Waiting)
  6. 终止状态(Terminated)

这 6 种状态构成了 Java 线程的完整生命周期,线程在运行过程中会在这些状态之间转换(除了新建和终止状态,其他状态可相互转换)。

2. 6 种核心状态详细说明

1. 新建状态(New)

  • 定义 :当创建了Thread类的实例对象,但尚未调用start()方法时,线程处于新建状态。
  • 核心特征 :
    1. 此时线程仅为一个 Java 对象,未被 JVM 真正启动,也未分配操作系统级别的线程资源(如栈空间、程序计数器等)。
    2. 线程尚未进入线程调度队列,无法被 CPU 调度执行。
  • 触发条件 :通过new Thread(...)创建线程实例(无论继承 Thread 类还是实现 Runnable 接口),未调用start()。
  • 示例 :Thread thread = new Thread(() -> {});(此时 thread 处于 New 状态)
  • 状态转换 :仅能通过调用thread.start()方法,从「新建状态」转换为「可运行状态(Runnable)」,且start()方法仅能调用一次(重复调用会抛出IllegalThreadStateException)。

2.可运行状态(Runnable)

  • 定义 :调用start()方法后,线程进入可运行状态,这是 Java 虚拟机层面合并后的状态,对应操作系统层面的两个状态:就绪状态 和 运行状态。
  • 核心特征 :
    1. 就绪状态:线程已被 JVM 启动,分配了系统资源,加入线程调度队列,等待 CPU 分配时间片(此时线程并未真正执行)。
    2. 运行状态:CPU 为线程分配了时间片,线程正在执行run()方法中的业务逻辑。
    3. Java 虚拟机不区分这两种状态,统一归为Runnable,因为线程在就绪和运行之间的切换由 CPU 调度器决定,JVM 无法干预。
  • 触发条件 :
    1. 新建状态线程调用start()方法,进入 Runnable 状态(就绪)。
    2. 阻塞状态 / 等待状态 / 超时等待状态的线程被唤醒 / 获取锁 / 超时后,会进入 Runnable 状态(就绪)。
  • 状态转换 :
    1. Runnable → Blocked:线程竞争synchronized对象锁失败时(等待锁释放)。
    2. Runnable → Waiting:调用无参的Object.wait()、Thread.join()、LockSupport.park()方法。
    3. Runnable → Timed_Waiting:调用带超时参数的Thread.sleep(long)、Object.wait(long)等方法。
    4. Runnable → Terminated:run()方法正常执行完毕,或线程因未捕获异常终止。

3.阻塞状态(Blocked)

  • 定义 :线程因 ** 等待获取对象监视器锁(synchronized 锁)** 而被阻塞的状态,仅与synchronized关键字相关。
  • 核心特征 :
    1. 线程此时无法执行代码,只能等待其他持有synchronized锁的线程释放锁。
    2. 该状态仅针对synchronized锁,若使用java.util.concurrent包下的 Lock 锁(如 ReentrantLock),线程竞争锁失败时会进入「等待状态(Waiting)」,而非 Blocked 状态。
  • 触发条件 :
    1. 线程尝试进入synchronized方法 / 代码块,而锁已被其他线程持有。
  • 状态转换 :
    1. Blocked → Runnable:持有synchronized锁的线程释放锁,且当前线程成功获取到锁。
    2. Blocked → Terminated:线程在阻塞期间被中断(极少发生,或线程对象被回收)。

4.等待状态(Waiting)

  • 定义 :线程进入无超时时间的等待状态,若无其他线程主动唤醒,该线程会一直等待,永不自动返回可运行状态。
  • 核心特征 :
    1. 等待状态是 "无限期" 的,依赖其他线程的显式唤醒操作。
    2. 进入该状态的线程会释放已持有的锁(若有),以便其他线程执行。
  • 触发条件(常见方法) :
    1. 调用Object.wait()方法(无参):需在synchronized代码块中调用,线程释放对象锁,进入等待。
    2. 调用Thread.join()方法(无参):等待被 join 的线程执行完毕,当前线程进入等待。
    3. 调用LockSupport.park()方法:无锁关联的等待,需通过LockSupport.unpark(Thread)唤醒。
  • 状态转换 :
    1. Waiting → Runnable:
      • 对于Object.wait():其他线程调用该对象的Object.notify()或Object.notifyAll()方法唤醒当前线程。
      • 对于Thread.join():被 join 的线程执行完毕,当前线程自动唤醒。
      • 对于LockSupport.park():其他线程调用LockSupport.unpark(当前线程)唤醒。
    2. Waiting → Terminated:线程在等待期间被中断,或线程对象被回收。

5.超时等待状态(Timed_Waiting)

  • 定义 :线程进入有明确超时时间的等待状态,无需其他线程主动唤醒,超时时间到达后会自动返回可运行状态;也可在超时前被其他线程主动唤醒。
  • 核心特征 :
    1. 等待有 "时间限制",是 Waiting 状态的超时版本。
    2. 进入该状态的线程同样会释放已持有的锁(除Thread.sleep(long)外,sleep方法不会释放锁)。
  • 触发条件(常见方法) :
    1. 调用Thread.sleep(long millis):线程休眠指定时间,不释放任何锁,超时后自动唤醒。
    2. 调用Object.wait(long millis)(带参):在synchronized代码块中调用,释放对象锁,超时自动唤醒或被notify()/notifyAll()唤醒。
    3. 调用Thread.join(long millis)(带参):等待被 join 的线程执行完毕,或超时后自动唤醒。
    4. 调用LockSupport.parkNanos(long nanos)/LockSupport.parkUntil(long deadline):带超时的 park 操作。
  • 状态转换 :
    1. Timed_Waiting → Runnable:
      • 超时时间到达,线程自动唤醒。
      • 超时前被其他线程主动唤醒(如Object.notify()、LockSupport.unpark())。
    2. Timed_Waiting → Terminated:线程在超时等待期间被中断,或线程对象被回收。

6.终止状态(Terminated)

  • 定义:线程的生命周期完全结束,线程不再具备执行能力,也无法转换为其他任何状态。
  • 核心特征 :
    1. 线程的run()方法执行流程终止,线程对象可能还存在(被 GC 回收前),但线程本身已失效。
    2. 终止后的线程无法再次调用start()方法(调用会抛出IllegalThreadStateException)。
  • 触发条件(两种场景) :
    1. 正常终止:run()方法中的业务逻辑全部执行完毕,线程正常退出。
    2. 异常终止:线程在执行run()方法时抛出未被捕获的异常(如 NullPointerException、InterruptedException 等),导致线程被迫终止。
  • 状态转换:无后续状态转换,线程生命周期结束。

3.关键区分

  1. Runnable 状态 vs 操作系统就绪 / 运行状态:Java 的 Runnable 是合并状态,包含操作系统的 "就绪(等待 CPU 调度)" 和 "运行(正在执行)" 两种状态,由 CPU 调度器负责切换,JVM 不感知。
  2. Blocked vs Waiting vs Timed_Waiting :
    • Blocked:仅因竞争synchronized锁阻塞,有明确的 "锁等待" 目标。
    • Waiting:无超时,需主动唤醒,释放锁(大部分场景)。
    • Timed_Waiting:有超时,可自动唤醒或主动唤醒,sleep()不释放锁,其他如wait(long)释放锁。
  3. sleep() vs wait() :
    • sleep(long):属于 Timed_Waiting,不释放锁,静态方法(针对当前线程)。
    • wait()/wait(long):分别属于 Waiting/Timed_Waiting,释放对象锁,实例方法(针对具体对象),需在synchronized中调用。
相关推荐
夜晚回家2 小时前
Python 赋值运算符详解 | 从 = 到 +=,一张记账单讲清复合赋值
开发语言·python
\光辉岁月/2 小时前
6.数组工具类
java·数组·数组工具类
布莱克6052 小时前
QtWidgets 与 QML:两大 UI 开发框架的全面对比
开发语言·c++·qt·ui·架构
hai_android2 小时前
Android 设备锁定:Kiosk Mode 实战
android·java·android jetpack
张伯毅3 小时前
External Shuffle Service(spark3_shuffle):原理、配置与排障
java·开发语言·spark·yarn
geovindu3 小时前
rust: Template Method Pattern
开发语言·后端·设计模式·rust·行为模式·模板方法模
high一马平川3 小时前
# SystemUI Dagger2 精读 · 第 2 章 @Inject:1200 处构造注入,和 1 处例外
android·java·kotlin
小陈从不会敲代码3 小时前
JavaEE面试复习(多线程-初阶)
java·面试·java-ee
周厉3 小时前
酒店PMS系统设计:房型房价管理与动态价格优先级实现(Java/MySQL)
java·数据库·pms酒店系统
一碗白开水一3 小时前
Day 5 · 弹性 | 微观
java·前端·数据库