目录

前言

概述

主动阻塞/唤醒

代码示例

实现

为什么必须在同步块中使用

计时等待是如何实现的

被动阻塞/唤醒

为什么要有被动阻塞/唤醒

实现(锁升级)


前言

JAVA多线程相关的内容很多很杂,但工作中用到的频率不高,用到的面也不全,导致是学了又忘忘了又学,在面对并发的场景下没办法很好的去解决问题,我相信不止一个人会存在以上这样的情况。出现这样的问题,是因为没有对多线程建立成体系的认识。接下来博主就将用一个系列去带大家去建立这个体系,博主有信心,这个系列能让大家从此对JAVA多线程体系有丝滑且根深蒂固的认识。

JAVA多线程部分的内容,无非分为三部分:

  • 线程的操作

    • 线程状态模型

    • 线程基础操作:创建线程、阻塞/唤醒、等待/唤醒

  • 线程安全问题,即JMM

  • 一系列的线程同步工具、线程编排工具

本文先讲第一部分:线程的操作。

当然这一部分只会讲线程的状态操作,不会去将怎么创建线程、结束线程这些基础操作,相信大家都有一定基础看这篇文章才是有益的。

概述

线程的操作这一部分无非内容就是:

  • JAVA中的线程状态模型

  • 线程的基本操作(新建、阻塞、等待)。

如果只是按照顺序来聊一遍,那和市面上大多数教程也没什么区别了,我们要建立关于线程操作这一部分的体系认识,才能从思想上彻底吃透,做到内化于心,这样怎么都不会忘记。JAVA线程操作部分的体系认识:

1.线程状态的控制是线程的核心操作

线程存在的根本意义本身就在于控制程序的执行,线程上跑的是程序,通过控制线程状态来实现对程序执行的控制,想暂停执行就阻塞当前线程,想要继续执行就让当前线程处于“就绪”状态去等待CPU的时间片。所以线程最核心的就是对于它的控制操作,即线程的状态操作。

2.JAVA的线程状态也是基于操作系统的原生状态,只是根据自己的需要将阻塞拆分成了多种

JAVA作为一门编程语言,它是运行在操作系统上的,所以他的线程对应的就是操作系统的真实线程,操作系统原生的线程状态有三种:就绪、运行、阻塞。JAVA自然不能违背这三种基础状态,JAVA只是将阻塞状态拆成了几种:轻量级阻塞(等待、计时等待)、重量级阻塞(阻塞)。

为什么要拆成轻量级阻塞和重量级阻塞?JAVA作为一门面向应用开发的语言,要提供线程操控能力,就要允许开发者来手动阻塞/唤醒线程,轻量级阻塞就是是给开发者调用Object的wait()和notify()来手动阻塞/唤醒线程的。重量级阻塞是因为手动操作阻塞/唤醒需要一个绝对线程安全的环境,即需要一个同步块,JDK提供了synchronized 原语,用来保证资源绝对被单一线程持有,从而创造出一个同步块出来。可以理解为两者底层都是调用系统调用对线程进行了真正的阻塞,轻量级阻塞是主动阻塞,重量级阻塞是由JVM层面来控制的阻塞。

3.线程的核心操作是阻塞/唤醒

在JAVA的多线程编程中,我们无非就是通过操作线程“阻塞/唤醒”来实现对线程的控制,这里面无非可做的就两件事:

  • 通过wait()和notify来实现主动的阻塞/唤醒

  • 通过synchronized关键字来实现被动的阻塞/唤醒

所以搞清楚JAVA线程的阻塞,其实就搞明白了JAVA多线程的核心操作。

主动阻塞/唤醒

代码示例

api:

  • wait(),等待notify/notifyAll唤醒

  • wait(long timeout),计时等待,到时自动唤醒,也可被notify/notifyAll唤醒

  • notify,唤醒,随机选择一个阻塞的线程唤醒

  • notifyAll,唤醒所有阻塞的线程,让他们去自由争抢

public class WaitNotifyExample {private final Object lock = new Object();private boolean condition = false;
​public void producer() throws InterruptedException {synchronized (lock) {System.out.println("生产者线程开始...");// 模拟生产耗时Thread.sleep(2000);condition = true;System.out.println("生产者完成,通知消费者...");lock.notify(); // 或者使用 notifyAll() 唤醒所有等待线程}}
​public void consumer() throws InterruptedException {synchronized (lock) {System.out.println("消费者线程开始,等待通知...");while (!condition) { // 使用 while 防止虚假唤醒lock.wait(); // 释放锁并等待}System.out.println("消费者被唤醒,继续执行...");condition = false;}}
​public static void main(String[] args) {WaitNotifyExample example = new WaitNotifyExample();new Thread(() -> {try {example.consumer();} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {try {example.producer();} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

实现

主动的阻塞/唤醒,是为了提供灵活的线程编排能力,能灵活的控制线程的执行进度,既然要足够灵活,那么就要求线程能被主动阻塞在任何地方,于是JVM选择能让线程被阻塞在任何类上。所以JVM会为每个对象维护一个监视器(Monitor),Java的wait()和notify()机制是通过JVM底层的监视器(Monitor)实现的。

class ObjectMonitor {private Thread owner;              // 当前持有锁的线程private int recursion;             // 重入次数private Queue<Thread> entryList;   // 等待获取锁的线程队列private Queue<Thread> waitSet;     // 等待条件的线程集合private Object object;             // 关联的Java对象private PlatformEvent platformEvent; // 平台相关的同步原语
}

【question】为什么要有两个队列?

waitSet里面存的是blocked(阻塞状态)的线程,entryList里面存的是可执行的线程,等待操作系统分配时间片。被阻塞的线程由waitSet唤醒,进入entryList,等待操作系统时间片来执行。

wait()时的队列操作:

  • 保存状态:保存当前线程的锁重入信息

  • 释放锁:将监视器的owner设为null,重入计数归零

  • 加入waitSet:将线程加入等待集合队列,状态变为WAITING

  • 系统调用:调用操作系统原语阻塞线程

  • 移出调度:线程从操作系统的运行队列中移除

notify()时的队列操作:

  • 检查waitSet:查看是否有线程在等待

  • 选择线程:从waitSet队列中选择一个等待线程

  • 移动队列:将选中线程从waitSet移到entryList

  • 改变状态:线程状态从WAITING变为BLOCKED

  • 系统调用:调用操作系统原语唤醒线程

为什么必须在同步块中使用

 private final Object lock = new Object();private boolean flag = false;//错误的并发代码(伪代码,实际会抛异常)public void problematicScenario() {// 线程A执行:if (!flag) { // 检查条件// 此时发生线程切换// 线程B执行并设置flag=true,调用notify()// 线程A恢复执行,调用wait() -> 永远等待!lock.wait(); // 错过了通知,永远阻塞}}//正确的同步版本public void correctScenario() throws InterruptedException {synchronized (lock) {// 检查和等待是原子操作while (!flag) {lock.wait(); // 不会错过通知}}}

计时等待是如何实现的

核心流程:

  • 验证监视器所有权 - 检查当前线程是否持有锁

  • 释放监视器锁 - 原子性地释放对象锁

  • 加入等待队列 - 线程加入对象的wait_set队列

  • 系统级阻塞 - 调用操作系统原语(futex/park/wait)阻塞线程

  • 超时管理 - 启动定时器监控超时

  • 等待唤醒 - 等待notify/notifyAll调用或超时到期

  • 重新竞争锁 - 被唤醒后重新获取监视器锁

  • 恢复执行 - 继续执行wait()后面的代码

如何实现超时管理:

  • 操作系统负责超时 (主流实现)

  • JVM调用带超时参数的系统调用

  • 操作系统内核维护定时器

  • 内核自动唤醒超时的线程

被动阻塞/唤醒

为什么要有被动阻塞/唤醒

被动阻塞可以理解为JDK提供的原语级别的能力,用来保证资源绝对被单一线程持有。

为什么要提供这种原语级别的能力:

因为我们仔细想想就能想到纯靠编码去进行主动阻塞是无法保证线程安全的。不管什么写法都不能保证wait()一定是线程安全的。所以JDK要自己提供一个原语来保证创造一个线程安全的环境,也就是创造一个同步区域、同步块。这就是JDK提供的(被动阻塞/唤醒)同步原语:synchronized关键字。

实现(锁升级)

synchronized 关键字用于实现线程同步,它可以保证同一时刻只有一个线程执行被synchronized 修饰的代码块或方法。

底层实现原理

  1. 对象头(Mark Word) 每个 Java 对象在内存中都有一个对象头,对象头中包含锁信息,用于实现 synchronized。

  2. 锁的四种状态 无锁状态:对象未被任何线程锁定 偏向锁:偏向第一个访问对象的线程,减少同一线程获取锁的开销 轻量级锁:当存在多个线程竞争但竞争不激烈时使用 重量级锁:当线程竞争激烈时,会阻塞未获取到锁的线程

  3. 锁升级过程 无锁 → 偏向锁 → 轻量级锁 → 重量级锁(单向升级)

无锁:

当同步代码首次被一个线程访问,那么就会在Mark Word记录该线程的ID,从无锁状态(001)变成偏向锁(101)。

img

偏向锁:

当下一次同步代码被访问时,那么就会检测该线程ID与锁的Mark Word 中的线程ID是否是相同。

相同:则直接进入同步代码,因为之前没有释放锁

不同:表示发生了竞争,会尝试使用CAS来替换Mark Word里面的线程ID。竞争成功则会替换Mark Word 里面的线程ID,竞争失败可能会变成轻量级锁。

img

轻量级锁:

当有第二个线程尝试获取锁时,偏向锁升级为轻量级锁,两个线程自由争抢,没抢到的就CAS自旋等待。

img

重量级锁:

当自旋超过一定次数(默认10次)或等待线程数超过CPU核心数的一半,锁升级为重量级锁,这时候会指向一个监视器对象,这个监视器对象用集合的形式,来登记和管理排队的线程。

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

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

相关文章

UE5多人MOBA+GAS 46、制作龙卷风技能

文章目录创建龙卷风GA创建蒙太奇创捷一系列GE添加数据表添加到角色中创建龙卷风GA GA_Tornado 添加标签 // 龙卷风冷却CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Tornado_Cooldown)// 通用技能伤害CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Generic_Dama…

如何在ubuntu下安装libgpiod库

以下是关于如何在ubuntu下安装libgpiod库的两种安装方式以及遇到ubuntu存在多个工具链导致编译失败的解决方法。如果想要自由选择使用不同版本的libgpiod&#xff0c;可以选择手动编译安装方式&#xff0c;系统安装默认1.6.3版本(ubuntu22.04)。手动编译安装1、在github上下载要…

qt vs2019编译QXlsx

1、安装ActivePerl2、打开pro文件&#xff0c;直接编译即可第一个简单实例&#xff1a;#include "xlsxcellrange.h" #include "xlsxchart.h" #include "xlsxchartsheet.h" #include "xlsxdocument.h" #include "xlsxrichstring.h…

计算机存储器分类和层次结构详解

存储器是计算机系统的核心部件之一&#xff0c;其核心功能是存储程序&#xff08;指令&#xff09;和数据&#xff0c;是冯诺依曼体系结构“存储程序”概念的物质基础。它直接关系到计算机系统的性能、容量和成本。 存储器核心内容总览表分类维度存储器层级技术实现速度容量成本…

通过rss订阅小红书,程序员将小红书同步到自己的github主页

title: 通过rss订阅小红书&#xff0c;程序员将小红书同步到自己的github主页 tags: 个人成长 categories:杂谈最近在做一些新的尝试&#xff0c;把文本的内容转化为漫画和图片&#xff0c;方便大众阅读&#xff0c;恰好小红书很适合分发这些内容&#xff0c;于是我开通了小红书…

麒麟KylinOS V10-SP3 安装FastGPT

1. 操作系统环境CPU&#xff1a;20核 Xeon(R) Platinum 8457C 内存&#xff1a;64GB GPU&#xff1a;4090 操作系统&#xff1a;KylinOS-V10-SP32. 安装docker、docker-compose、fastgpt下载安装docker、docker-compose1. 下载docker docker 下载地址&#xff1a; https://do…

前端/在vscode中创建Vue3项目

Contenthtml input元素添加css样式使用js添加交互按钮点击提示输入框字符计数使用 npm 来管理项目包安装 Node.js初始化项目安装依赖包创建一个基于 Vite 的 Vue 项目创建项目进入项目目录安装依赖调用代码格式化工具启动开发服务器在浏览器中访问html input元素 <input ty…

HiSmartPerf使用WIFI方式连接Android机显示当前设备0.0.0.0无法ping通!设备和电脑连接同一网络,将设备保持亮屏重新尝试

在使用HiSmartPerf使用WIFI方式连接Android机时&#xff0c;如果出现无法ping通0.0.0.0的情况&#xff0c;可以尝试以下步骤解决问题&#xff1a;问了一下AI&#xff0c;给出的解答如下&#xff1a; 检查网络连接 &#xff1a;确保设备和电脑连接到同一局域网的Wi-Fi。可以在手…

SpringWeb是什么东西?

SpringWeb是个什么东西&#xff1f;SpringWeb是一个Java开发Web项目时的Web层框架。所谓Web层&#xff0c;就是直接和用户打交道的框架&#xff0c;用户(User)也就是顾客&#xff0c;顾客就是上帝&#xff0c;我们说是Web项目&#xff0c;通常也就是说B/S架构的项目&#xff0c…

docker+nginx+keepalived+openappsec+web ui+crowdsec部署安全代理

docker+nginx+keepalived+openappsec+web ui+crowdsec部署安全代理 一、环境介绍 二、基础环境安装 1、优化系统参数 2、安装docker 3、创建容器网络 4、安装测试容器(可选) 三、安装nginx 1、拉取镜像 2、创建映射目录 3、准备默认配置文件 4、证书文件准备 5、启动nginx容器…

自动驾驶中安全相关机器学习功能的可靠性定义方法

摘要当前标准无法涵盖高自动化驾驶中基于机器学习功能的安全需求。由于神经网络的不透明性&#xff0c;一些自动驾驶功能无法按照 V 模型进行开发。这些功能需要对标准进行扩展。本文聚焦这一空白&#xff0c;为这类功能定义了功能可靠性&#xff0c;以帮助未来的标准控制基于机…

css实现圆角+边框渐变+背景半透明

ui小姐姐经常搞一些花里胡哨的东西&#xff0c;圆角边框渐变背景半透明&#xff0c;虽然每个都可以弄&#xff0c;但是合在一起真的不好弄&#xff0c;主要是因为通过border–image设置的边框渐变&#xff0c;无法使用圆角&#xff0c;下面是自己搜索整理的一些可以的方案。 方…

tree组件(几种不同分叉树Vue3)

效果图&#xff1a; 基础树组件&#xff08;本人博客里面有&#xff09; https://blog.csdn.net/xfy991127/article/details/140346861?spm1001.2014.3001.5501 下面是工作需求改造后 父组件 <template><div class"go-JJTree" id"tree-scroll&quo…

百度智能云x中科大脑:「城市智能体」如何让城市更会思考

近日&#xff0c;2025中关村论坛系列活动——中关村人工智能与未来城市论坛在中关村国家自主创新示范区展示中心举办。论坛上&#xff0c;发布了应用范式创新升级成果、智能体产品、可信数据空间成果等。 中科大脑联合百度智能云等伙伴共同打造并发布21个智能体产品&#xff0c…

在职老D渗透日记day16:sqli-labs靶场通关(第24关)二次注入 sqlmap自动注入没跑出来。。。

5.24.2.sqlmap自动注入第一个&#xff1a;登录页面&#xff08;1&#xff09;pb抓取http头POST /sqli-labs/Less-24/login.php HTTP/1.1 Host: 192.168.10.106 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0 Accept: text/ht…

Fanuc机器人EtherCAT通讯配置详解

1、EtherCAT简介EtherCAT&#xff0c;这一基于以太网的现场总线系统&#xff0c;以其开放架构和高速性能著称。CAT代表的是控制自动化技术&#xff08;Control Automation Technology&#xff09;的缩写&#xff0c;彰显了其在工业自动化领域的核心地位。作为确定性的工业以太网…

超酷炫的Three.js示例

今天写一个超级酷炫的Three.js示例&#xff0c;以下是文件源代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-sca…

从零开始大模型之实现GPT模型

从零开始大模型之从头实现GPT模型1.大语言模型整体架构2 大语言的Transformer模块2.1 层归一化2.2 GELU激活函数2.3 前馈神经网络2.4 快捷连接3 附录3.1 anacondapython环境搭建1.数据预处理&#xff1a;原始数据进行词元化&#xff0c;以及通过&#xff0c;依据词汇表生成ID编…

[1Prompt1Story] 滑动窗口机制 | 图像生成管线 | VAE变分自编码器 | UNet去噪神经网络

链接&#xff1a;https://github.com/byliutao/1Prompt1Story 这个项目是一个基于单个提示生成一致文本到图像的模型。它在ICLR 2025会议上获得了聚焦论文的地位。该项目提供了生成一致图像的代码、Gradio演示代码以及基准测试代码。 主要功能点: 使用单个提示生成一致的文本…

【GitHub开源AI精选】Sitcom-Crafter:北航联合港中文等高校打造的剧情驱动3D动作生成系统

系列篇章&#x1f4a5; No.文章1【GitHub开源AI精选】LLM 驱动的影视解说工具&#xff1a;Narrato AI 一站式高效创作实践2【GitHub开源AI精选】德国比勒费尔德大学TryOffDiff——高保真服装重建的虚拟试穿技术新突破3【GitHub开源AI精选】哈工大&#xff08;深圳&#xff09;…