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中调用。
相关推荐
Vincent_Vang2 小时前
多态 、抽象类、抽象类和具体类的区别、抽象方法和具体方法的区别 以及 重载和重写的相同和不同之处
java·开发语言·前端·ide
Fate_I_C2 小时前
Kotlin 中的 suspend(挂起函数)
android·开发语言·kotlin
周亚鑫2 小时前
vue3 js代码混淆
开发语言·javascript·ecmascript
陳10302 小时前
C++:vector(1)
开发语言·c++
棉晗榜2 小时前
WPF将程序集里面嵌入的资源文件下载到本机磁盘中,将项目中的文件下载到桌面
开发语言·wpf
花卷HJ2 小时前
Android 下载管理器封装实战:支持队列下载、取消、进度回调与自动保存相册
android·java
wanghowie2 小时前
01.01 Spring核心|IoC容器深度解析
java·后端·spring
人道领域2 小时前
【零基础学java】(Map集合)
java·开发语言
@淡 定2 小时前
Seata AT模式详细实例:电商下单场景
java