目录

一、线程的五种基本状态

二、线程从 RUNNABLE 进入阻塞 / 等待状态的三种典型场景

1. 调用sleep(long millis):进入 TIMED_WAITING 状态

2. 调用wait():进入 WAITING/TIMED_WAITING 状态

3. 等待 I/O 资源或获取锁失败:进入 BLOCKED 状态

三、线程从阻塞 / 等待状态回到 RUNNABLE 状态的底层原理

1. TIMED_WAITING 状态的唤醒(如sleep()超时)

2. WAITING/TIMED_WAITING 状态的唤醒(如wait()被唤醒)

对象监视器(Monitor)

wait()方法的底层执行流程

notify()/notifyAll()的底层执行流程

3. BLOCKED 状态的唤醒(如获取锁或 I/O 就绪)

四、总结:线程状态转换的核心逻辑


在多线程编程中,线程状态的转换是理解并发行为的核心。当运行中的线程因调用sleep()wait()或等待 I/O 等操作让出 CPU 时,会进入特定的阻塞状态;而从阻塞状态重新回到可运行状态的过程,更是体现了 JVM 对线程调度的精妙设计。本文将深入解析线程状态转换的底层机制,揭示线程如何在不同状态间切换。

一、线程的五种基本状态

根据 Java 线程模型,线程的生命周期包含五种状态,由Thread.State枚举定义:

  1. 新建(NEW):线程对象已创建但未调用start()方法。
  2. 可运行(RUNNABLE):线程已启动,正在 JVM 中运行或等待 CPU 调度。
  3. 阻塞(BLOCKED):线程等待获取 synchronized 锁(进入 synchronized 块 / 方法时未抢到锁)。
  4. 等待(WAITING):线程无限期等待被其他线程唤醒(如调用wait()join()无参方法)。
  5. 超时等待(TIMED_WAITING):线程在指定时间内等待(如sleep(1000)wait(1000))。
  6. 终止(TERMINATED):线程执行完毕或因异常退出。

核心转换场景:运行状态(RUNNABLE)的线程会因特定操作进入阻塞 / 等待状态,待条件满足后重新回到 RUNNABLE 状态。

二、线程从 RUNNABLE 进入阻塞 / 等待状态的三种典型场景

运行中的线程(RUNNABLE)在以下情况会让出 CPU,进入非运行状态:

1. 调用sleep(long millis):进入 TIMED_WAITING 状态

sleep()是 Thread 类的静态方法,作用是让当前线程暂停执行指定毫秒数。调用后线程状态变化为:
RUNNABLE → TIMED_WAITING

  • 特点
    • 线程不会释放已持有的锁(如 synchronized 锁)。
    • 暂停时间是相对精确的(受系统时钟和 CPU 调度影响)。
    • 常用于模拟延迟(如网络请求等待)。
public class SleepDemo {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {try {System.out.println("线程进入睡眠");Thread.sleep(3000); // 进入TIMED_WAITING状态System.out.println("线程唤醒");} catch (InterruptedException e) {e.printStackTrace();}});t.start();}
}

2. 调用wait():进入 WAITING/TIMED_WAITING 状态

wait()是 Object 类的方法,必须在synchronized块中调用,作用是让线程释放锁并等待被唤醒。调用后状态变化为:
RUNNABLE → WAITING(无参wait())或TIMED_WAITINGwait(long timeout)

  • 特点
    • 必须持有对象锁(synchronized),调用后立即释放锁。
    • 需其他线程调用同一对象的notify()/notifyAll()唤醒(或超时自动唤醒)。
    • 常用于线程间协作(如生产者消费者模型)。
public class WaitDemo {private static final Object lock = new Object();public static void main(String[] args) {new Thread(() -> {synchronized (lock) {try {System.out.println("线程进入等待");lock.wait(); // 进入WAITING状态System.out.println("线程被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}

3. 等待 I/O 资源或获取锁失败:进入 BLOCKED 状态

当线程等待外部资源(如磁盘 IO、网络请求)或尝试获取 synchronized 锁但失败时,会进入 BLOCKED 状态:
RUNNABLE → BLOCKED

  • 特点
    • 等待 I/O 时,线程暂停执行,直到资源就绪(如数据读取完成)。
    • 获取锁失败时,线程进入锁的 "入口集"(Entry Set)排队,直到锁被释放。
public class BlockedDemo {private static final Object lock = new Object();public static void main(String[] args) {// 线程1先获取锁new Thread(() -> {synchronized (lock) {try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }}}).start();// 线程2后启动,尝试获取锁失败,进入BLOCKED状态new Thread(() -> {synchronized (lock) { // 此处会阻塞System.out.println("线程2获取到锁");}}).start();}
}

三、线程从阻塞 / 等待状态回到 RUNNABLE 状态的底层原理

线程进入阻塞 / 等待状态后,并非永久停止,而是在特定条件满足后重新回到 RUNNABLE 状态。不同状态的唤醒机制底层原理不同:

1. TIMED_WAITING 状态的唤醒(如sleep()超时)

sleep(long millis)的唤醒依赖系统定时器

  • 底层流程

    1. 线程调用sleep()时,JVM 向操作系统注册一个定时事件(指定 millis 后触发)。
    2. 线程从 RUNNABLE 状态切换到 TIMED_WAITING,操作系统将其从 CPU 调度队列中移除。
    3. 定时时间到达后,操作系统触发事件,JVM 将线程重新放入 CPU 调度队列,状态切换为 RUNNABLE。
    4. 线程等待 CPU 调度,获取时间片后继续执行。
  • 特殊情况:若线程在sleep()期间被中断(调用interrupt()),会抛出InterruptedException并提前唤醒,状态直接回到 RUNNABLE。

2. WAITING/TIMED_WAITING 状态的唤醒(如wait()被唤醒)

要理解wait()notify()/notifyAll()的底层原理,需要从线程状态转换对象监视器(Monitor) 两个核心角度展开。这组方法是 Java 中实现线程间协作的基础,其底层依赖于 JVM 对对象锁的管理机制。

对象监视器(Monitor)

Java 中每个对象都隐式关联一个对象监视器(Monitor),也称为 “内置锁” 或 “管程”。它是实现同步和线程协作的核心数据结构,内部包含三个关键部分:

  1. 持有线程:当前获取到锁的线程(同一时间只能有一个线程持有)。
  2. 入口集(Entry Set):等待获取锁的线程集合(线程状态为BLOCKED)。
  3. 等待集(Wait Set):调用wait()后释放锁并等待被唤醒的线程集合(线程状态为WAITINGTIMED_WAITING)。

简单说,Monitor 就像一个 “休息室 + 会议室”:

  • 会议室(锁)同一时间只能有一个线程使用(持有锁的线程);
  • 入口集是等待进入会议室的线程队列;
  • 等待集是在会议室内主动暂停(调用wait())并临时退出到休息室的线程队列。
wait()方法的底层执行流程

当线程调用obj.wait()时,底层会执行以下操作(必须在synchronized块中调用,否则会抛IllegalMonitorStateException):

  1. 释放当前对象的锁
    调用wait()的线程必须已经持有obj的锁(即处于synchronized(obj)块中)。执行wait()后,线程会主动释放该锁,退出 “持有线程” 位置,让其他线程有机会获取锁。

  2. 进入对象的等待集(Wait Set)
    线程释放锁后,会从 “运行状态” 转入 “等待状态(WAITING)”,并被放入obj的等待集(休息室)中。此时线程不再参与 CPU 调度,处于阻塞状态。

  3. 等待被唤醒
    线程在等待集中休眠,直到其他线程调用obj.notify()obj.notifyAll(),才有可能被唤醒。若调用的是wait(long timeout),则超时后也会自动唤醒。

  4. 重新竞争锁
    被唤醒的线程不会立即执行,而是从等待集转移到入口集(Entry Set),重新参与锁的竞争。只有重新获取到obj的锁后,才能从wait()方法返回,继续执行后续代码。

  • 关键细节:被唤醒的线程必须重新竞争锁,因此wait()通常配合while循环检查条件(防止虚假唤醒):

    synchronized (obj) {while (条件不满足) { // 用while而非if,避免虚假唤醒obj.wait();}
    }
    
notify()/notifyAll()的底层执行流程

notify()notifyAll()用于唤醒等待集中的线程,底层流程如下:

  1. notify():从对象的等待集中随机选择一个线程,将其从等待集移到入口集(Entry Set),使其有机会重新竞争锁。
  2. notifyAll():将对象等待集中的所有线程全部移到入口集,让所有等待线程重新竞争锁。

注意

  • 调用notify()/notifyAll()的线程不会立即释放锁,而是要等当前synchronized块执行完毕后才释放。因此,被唤醒的线程需要等待唤醒它的线程释放锁后,才能重新竞争锁。
  • 若等待集中没有线程,notify()/notifyAll()调用无效果。

3. BLOCKED 状态的唤醒(如获取锁或 I/O 就绪)

  • 获取锁的 BLOCKED 线程

    1. 线程因未抢到 synchronized 锁进入 BLOCKED 状态,排队在锁的入口集(Entry Set)。
    2. 当持有锁的线程释放锁(退出 synchronized 块),JVM 从入口集中唤醒一个或多个线程(具体策略由 JVM 实现决定)。
    3. 被唤醒的线程竞争锁,成功后状态切换为 RUNNABLE。
  • 等待 I/O 的 BLOCKED 线程

    1. 线程发起 I/O 请求后,进入 BLOCKED 状态(如读取文件时等待磁盘数据)。
    2. 操作系统负责处理 I/O 操作,完成后向 JVM 发送I/O 完成事件
    3. JVM 接收到事件后,将线程状态切换为 RUNNABLE,等待 CPU 调度。

四、总结:线程状态转换的核心逻辑

线程状态转换的本质是JVM 与操作系统协作的资源调度过程

  1. 主动让出 CPUsleep()wait()等方法让线程暂时放弃执行权,进入特定状态。
  2. 阻塞原因解除:超时、被唤醒、资源就绪等条件触发时,线程重新获得执行资格。
  3. 竞争 CPU 时间片:所有回到 RUNNABLE 状态的线程,最终需通过操作系统的 CPU 调度获得执行机会。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93825.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93825.shtml
英文地址,请注明出处:http://en.pswp.cn/web/93825.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面经整理-猿辅导-内容服务后端-java实习

部门管理系统设计 题目要求 设计部门 MySQL 数据表实现接口:根据中间部门 ID 获取其下属叶子部门 ID设计包含子节点列表的 Java 数据对象,并实现批量获取功能 一、MySQL 部门表设计 表结构 CREATE TABLE department (id BIGINT PRIMARY KEY AUTO_INCREME…

Openharmony之window_manager子系统源码、需求定制详解

1. 模块概述 Window Manager 模块是 OpenHarmony 操作系统的核心窗口管理系统,负责窗口的创建、销毁、布局、焦点管理、动画效果以及与硬件显示的交互。该模块采用客户端-服务端架构,提供完整的窗口生命周期管理和用户界面交互支持。 1.1架构总览 Window Manager Client 应…

《CDN加速的安全隐患与解决办法:如何构建更安全的网络加速体系》

CDN(内容分发网络)作为提升网站访问速度的关键技术,被广泛应用于各类互联网服务中。然而,在享受加速优势的同时,CDN也面临诸多安全隐患。本文将解析常见的CDN安全问题,并提供实用的解决办法,帮助…

【Linux指南】GCC/G++编译器:庖丁解牛——从源码到可执行文件的奇幻之旅

不只是简单的 gcc hello.c 每一位Linux C/C++开发者敲下的第一行编译命令,几乎都是 gcc hello.c -o hello 或 g++ hello.cpp -o hello。这像一句神奇的咒语,将人类可读的源代码变成了机器可执行的二进制文件。但在这条简单的命令背后,隐藏着一个如同精密钟表般复杂的多步流…

地区电影市场分析:用Python爬虫抓取猫眼_灯塔专业版各地区票房

在当今高度数据驱动的影视行业,精准把握地区票房表现是制片方、宣发团队和影院经理做出关键决策的基础。一部电影在北上广深的表现与二三线城市有何差异?哪种类型的电影在特定区域更受欢迎?回答这些问题,不能再依赖“拍脑袋”和经…

Spark03-RDD02-常用的Action算子

一、常用的Action算子 1-1、countByKey算子 作用:统计key出现的次数,一般适用于K-V型的RDD。 【注意】: 1、collect()是RDD的算子,此时的Action算子,没有生成新的RDD,所以,没有collect()&…

[Android] 显示的内容被导航栏这挡住

上图中弹出的对话框的按钮“Cancel/Save”被导航栏遮挡了部分显示&#xff0c;影响了使用。Root cause: Android 应用的主题是 Theme.AppCompat.Light1. 修改 AndroidManifest.xml 将 application 标签的 android:theme 属性指向新的自定义主题&#xff1a;<applicationandr…

分贝单位全指南:从 dB 到 dBm、dBc

引言在射频、音频和通信工程中&#xff0c;我们经常会在示波器、频谱仪或测试报告里看到各种各样的dB单位&#xff0c;比如 dBm、dBc、dBV、dBFS 等。它们看起来都带个 dB&#xff0c;实则各有不同的定义和参考基准&#xff1a;有的表示相对功率&#xff0c;有的表示电压电平&a…

怎么确定mysql 链接成功了呢?

asyncio.run(test_connection()) ✗ Connection failed: cryptography package is required for sha256_password or caching_sha2_password auth methods 根据你提供的错误信息,问题出现在 MySQL 的认证插件和加密连接配置上。以下是几种解决方法: 1. 安装 cryptography 包…

(5)软件包管理器 yum | Vim 编辑器 | Vim 文本批量化操作 | 配置 Vim

Ⅰ . Linux 软件包管理器 yum01 安装软件在 Linux 下安装软件并不像 Windows 下那么方便&#xff0c;最通常的方式是去下载程序的源代码并进行编译&#xff0c;从而得到可执行程序。正是因为太麻烦&#xff0c;所以有些人就把一些常用的软件提前编译好并做成软件包&#xff0c;…

VGG改进(3):基于Cross Attention的VGG16增强方案

第一部分&#xff1a;交叉注意力机制解析1.1 注意力机制基础注意力机制的核心思想是模拟人类的选择性注意力——在处理信息时&#xff0c;对重要部分分配更多"注意力"。在神经网络中&#xff0c;这意味着模型可以学习动态地加权输入的不同部分。传统的自注意力(Self-…

代理ip平台哪家好?专业代理IP服务商测评排行推荐

随着互联网的深度发展&#xff0c;通过网络来获取全球化的信息资源&#xff0c;已成为企业与机构在竞争中保持优势的一大举措。但想要获取其他地区的信息&#xff0c;可能需要我们通过代理IP来实现。代理IP平台哪家好&#xff1f;下文就让我们从IP池资源与技术优势等细节&#…

PWA》》以京东为例安装到PC端

如果访问 浏览器右侧出现 安装 或 点击这个 也可以完成安装桌面 会出现 如下图标

Linux系统:C语言进程间通信信号(Signal)

1. 引言&#xff1a;从"中断"到"信号"想象一下&#xff0c;你正在书房专心致志地写代码&#xff0c;这时厨房的水烧开了&#xff0c;鸣笛声大作。你会怎么做&#xff1f;你会暂停&#xff08;Interrupt&#xff09; 手头的工作&#xff0c;跑去厨房关掉烧水…

LoRa 网关组网方案(二)

LoRa 网关组网方案 现有需求&#xff1a;网关每6秒接收不同节点的数据&#xff0c;使用SX1262芯片。 以下是完整的组网方案&#xff1a;1. 网络架构设计 采用星型拓扑&#xff1a; 网关&#xff1a;作为中心节点&#xff0c;持续监听多个信道节点&#xff1a;分布在网关周围&am…

服装外贸系统软件怎么用才高效防风险?

服装外贸系统软件概述 服装外贸系统软件&#xff0c;如“艾格文ERP”&#xff0c;是现代外贸企业不可或缺的管理工具。它整合了订单处理、库存管理、客户资源保护、财务控制等多功能模块&#xff0c;旨在全面提升业务运营效率。通过系统化的管理方式&#xff0c;艾格文ERP能够从…

【沉浸式解决问题】peewee.ImproperlyConfigured: MySQL driver not installed!

目录一、问题描述二、原因分析三、解决方案✅ 推荐&#xff1a;安装 pymysql&#xff08;纯 Python&#xff0c;跨平台&#xff0c;安装简单&#xff09;✅ 可选&#xff1a;安装 mysqlclient&#xff08;更快&#xff0c;但需要本地编译环境&#xff09;✅ 总结四、mysql-conn…

C++进阶-----C++11

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

(论文速读)航空轴承剩余寿命预测:多生成器GAN与CBAM融合的创新方法

论文题目&#xff1a;Remaining Useful Life Prediction Approach for Aviation Bearings Based on Multigenerator Generative Adversarial Network and CBAM&#xff08;基于多发生器生成对抗网络和CBAM的航空轴承剩余使用寿命预测方法&#xff09;期刊&#xff1a;IEEE TRAN…

3ds Max 流体模拟终极指南:从创建到渲染,打造真实液体效果

流体模拟是提升 3D 场景真实感的重要技术之一。无论是模拟飞瀑流泉、杯中溢出的饮料&#xff0c;还是黏稠的蜂蜜或熔岩&#xff0c;熟练掌握流体动力学无疑能为你的作品增色不少。本文将以 3ds Max 为例&#xff0c;系统讲解流体模拟的创建流程与渲染方法&#xff0c;帮助你实现…