JUC工具类-LockSupport概述
前言
多线程并发场景中,时常需要线程协同,故而需要对当前线程进行阻塞,并唤醒需要协同的线程来一起完成任务。
通常处理方式有三种:
1)Synchronized加锁的线程
使用Object类下所提供的方法:
- wait():让当前线程处于等待状态,并释放当前拥有的锁;
- notify():随机唤醒等待该锁的其他线程,重新获取锁,并执行后续的流程,只能唤醒一个线程;
- notifyAll():唤醒所有等待该锁的线程(锁只有一把,虽然所有线程被唤醒,但所有线程需要排队执行)
2)Lock加锁的线程
Condition 类下的锁提供的方法:
- await():对应 Object 的 wait() 方法,线程等待;
- signal():对应 Object 的 notify() 方法,随机唤醒一个线程;
- signalAll():对应 Object 的 notifyAll() 方法,唤醒所有线程。
3)以上两种加锁的场景
使用JUC中LockSupport阻塞工具类的方法:
- LockSupport.park():休眠当前线程。
- LockSupport.unpark(线程对象):唤醒某一个指定的线程。
概述
LockSupport来自于JDK1.5,位于JUC包的locks子包,是一个非常方便实用的线程阻塞工具类,它定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,可以在线程内任意位置让线程阻塞、唤醒。
在AQS框架的源码中,当需要阻塞或唤醒一个线程的时候,都会使用LockSupport工具来完成。LockSupport 和 CAS 是Java并发包中并发工具(锁和其他同步类)控制机制的实现基础,而这两个基础其实又是依赖Unsafe类,然而Unsafe只是维护了一系列本地方法接口,因此真正的实现是在HotSpot的源码中,而HotSpot是采用C++来实现的!
AQS框架是JUC中实现同步组件的基石,而LockSupport可以说是AQS框架的基石之一。
LockSupport的特征和原理
特征
1、LockSupport是非重入的。因为调用一次park方法,线程就被阻塞了。
**2、LockSupport的park阻塞、unpark唤醒的调用不需要任何条件对象,也而不需要先获取什么锁。**也就是说LockSupport只与线程绑定,就可以降低代码耦合性。
**3、park阻塞与unpark唤醒的调用顺序可以颠倒,不会出现死锁,并且可以重复多次调用unpark。**但是ThreadGroup类的stop和resume方法如果顺序反了,就会出现死锁现象。
**4、park支持中断唤醒,但是不会抛出InterruptedException异常。**意思就是使用Thread.interrupted() 可以使线程中断,会使park失效。
原理
每个线程都与一个许可(permit)关联。unpark函数为线程提供permit,线程调用park函数则等待并消耗permit。permit默认是0,调用一次unpark就变成1,再调用一次park会消费permit,也就是将1变成0,park会立即返回。
如果原来没有permit,那么调用park会将相关线程阻塞在调用处,等待一个permit,这时调用unpark又会把permit置为1,使得阻塞的线程被唤醒。每个线程都有自己的permit,但是permit最多持有一个,重复调用unpark也不会积累。
LockSupport.park和LockSupport.unpark不会引发的死锁问题,因为由于许可的存在,即使unpark发生在park之前,也可以使得下一次的park操作立即返回。
和Object.wait相比,LockSupport.park不需要先获得某个对象的锁,也不会抛出InterruptedException 异常。
和synchronized相比,LockSupport.park()阻塞的线程可以被中断阻塞,但是不会抛出异常,并且中断之后不会清除中断标志位。被park阻塞的线程处于WAITING状态,超时park阻塞的线程则处于TIMED_WAITING状态。
LockSupport的方法解析
LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark方法来唤醒一个被阻塞的线程。
park
除非许可证permit可用,否则出于线程调度目的禁用当前线程。 如果许可证可用,则该许可证被消耗,呼叫立即返回;否则,出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
- 其他线程以当前线程为目标调用 unpark;
- 或其他线程中断当前线程;
- 或呼叫错误地(即,没有原因地)返回。
此方法不报告导致方法返回的原因。调用方应首先重新检查导致线程停止的条件。调用方还可以在返回时确定线程的中断状态。
unpark
使给定线程的许可证permit可用(如果尚未可用)。如果线程在 park上被阻塞,那么它将解除阻塞。否则,它对 park 的下一次呼叫保证不会被阻塞。如果给定的线程尚未启动,则不能保证此操作有任何效果。
源码分析
在 LockSupport.park()的底层主要是调用 Unsafa 类的 park方法,每个线程对象都有一个 Parker 实例,这个实例内部有提供park和unpark方法。
Unsafe类
Unsafe这个类中有许多的native方法,通过字段偏移量(类似于C的指针),提供了Java语言与底层系统进行交互的接口,通过Unsafe可以直接操作底层系统,它具有直接内存管理、线程阻塞&唤醒的支持、CAS操作的支持、直接操作类、对象、变量等强大的功能。
JavaThread
JavaThread内部具有一个threadOb属性,这个属性实际上就是保存这着Java层面的一个Thread对象,而JavaThread继承了Thread,继承了_osthread字段。那么一个JavaThread对象和一个OSThread对象对应,同时又和一个Thread对象对应,这样它们三个的就被联系起来了。因此实际上一个Java的Thread对应着一个OS线程。
Unsafe可以直接操作JVM和底层系统,因此,可以通过Thread是直接找到JavaThread实例进行操作,因此即使我们在Thread中没有找到“permit”,但是这个“permit”肯定是在Hotspot的源码中能就见到!
JavaThread内部还有一个Parker类型的_parker属性,这个Parker实际上就是用来实现Java中的LockSupport 的park 和unpark的,即实现单个线程的阻塞和唤醒,也就是JUC的中线程阻塞、唤醒在JVM层面的实现。
Parker有一个_counter字段,这个字段实际上就是我们常说的“许可”,并且默认初始化为0。我们调用的park、unpark方法,实际上是调用的Parker的同名方法。
PlatformParker
Parker源码中表示Parker继承了PlatformParker,由于Hotspot虚拟机为跨平台,针对不同操作系统有不同的实现,最常见的就是linux系统,以此来分析。
PlatformParker内部具有POSIX库标准的互斥量(锁)mutex和条件变量condition,那么实际上Parker的对于park和unpark的实现实际上就是用这两个工具实现的。
另外,PlatformParker还有一个cur_index属性,它的值为-1、0或者1,-1时初始化的值,调用park并返回的线程也会设置值为-1。如果不是-1,那么表示对应的parker中的条件变量上有线程被挂起,_cur_index等于0表示调用park相对时间的线程在第一个条件变量上被挂起,等于1则表示调用park绝对时间的线程在第二个条件变量上被挂起。
mutex与condition概述
实际上mutex与condition都是posix标准的用于底层系统线程实现线程同步的工具。 mutex被称为互斥量锁,类似于Java的锁,即用来保证线程安全,一次只有一个线程能够获取到互斥量mutex,获取不到的线程则可能会阻塞。而这个condition可以类比于java的Condition,被称为条件变量,用于将不满足条件的线程挂起在指定的条件变量上,而当条件满足的时候,再唤醒对应的线程让其执行。
可以理解为,mutex与condition来实现加锁、释放锁。如同synchronized与监视器对象一样。
Parker 和 Synchronized的ParkEvent 的区别
ParkEvent是用于Java级别的Synchronized关键字,Parker是JSR166来的并发工具集合,后面会统一使用ParkEvent。ParkerEvent 继承了PlatformEvent,基类PlatformEvent是特定于平台的,而ParkEvent则是平台无关的。而Parker 继承自PlatformParker。
- ParkerEvent中的park,unpark方法用于实现Java的object.wait()方法和object.notify()方法;
- Parker中的park,unpark方法用于实现Java的Locksupprt.park()方法和Locksupprt.unpark()方法;
park 方法源码分析
执行过程:
1、判断是否需要阻塞等待,如果已经是 _counter >0, 不需要等待,将 _counter = 0 , 返回
2、如果 1 不成立,构造当前线程的 ThreadBlockInVM ,检查 _counter > 0 是否成立,成立则将 _counter 设置为 0, unlock mutex 返回;
3、如果 2 不成立,更具需要时间进行不同的函数等待,如果等待正确返回,则将 _counter 设置为0, unlock mutex , park 调用成功。
parker 类的定义如下
- Parker 类继承 os::PlatformParker。
- 有一个 _counter 属性,可以理解为是否可以调用 park 方法的许可证,只有 _count > 0 的时候才能调用;
- 提供了公开的 park 和 unpark 方法
unpark 方法源码分析
执行过程:
- pthread_mutex_lock 获取锁
- _counter 设置为 1
- 判断 _counter 的旧值:
- 小于 1 时,调用 pthread_cond_signal 唤醒在 park 阻塞的线程;
- 等于 1 时,直接返回
总结
LockSupport是JDK1.5时提供的用于实现单个线程等待、唤醒机制的阻塞工具,也是AQS框架的基石之一,另外两个则是CAS操作、volatile关键字。
每个Java线程内部,其对应着OS线程,而对线程的操作,也就是对于操作系统的操作。JDK提供Unsafe类可以直接操作底层系统,它本身具有直接内存管理、线程阻塞&唤醒的支持、CAS操作的支持、直接操作类、对象、变量等强大的功能。Unsafe类关于线程阻塞、唤醒,有提供Parker类,Parker类继承PlatformParker。
PlatformParker内部具有POSIX库标准的互斥量(锁)mutex和条件变量condition,那么实际上Parker的对于park和unpark的实现实际上就是用这两个工具实现的。
LockSupport.park方法实质上就是调用了Unsafe类的Native方法,执行LockSupport.park方法不会释放此前获取到的synchronized锁或者lock锁,因为LockSupport的方法根本就与我们常说的“锁”无关。其本质依赖了mutex和Condition工具。
相关文章:
JUC工具类-LockSupport概述
前言 多线程并发场景中,时常需要线程协同,故而需要对当前线程进行阻塞,并唤醒需要协同的线程来一起完成任务。 通常处理方式有三种: 1)Synchronized加锁的线程 使用Object类下所提供的方法: wai…...
大数据:AI大模型对数据分析领域的颠覆(文末送书)
随着数字化时代的到来,大数据已经成为了各行各业中不可或缺的资源。然而,有效地分析和利用大数据仍然是一个挑战。在这个背景下,OpenAI推出的Code Interpreter正在对数据分析领域进行颠覆性的影响。 如何颠覆数据分析领域?带着这…...
CEdit 选中文字实时更新到另一个控件中
有时候,我们会遇到需求,软件中需要让选中一个CEdit控件中的文字实时更新到另一个控件中,实现效果如下所示: 代码如下: BOOL CEditDemoDlg::PreTranslateMessage(MSG* pMsg) { CEdit* pOldEdit (CEdit*)GetDlgIte…...
Word导出创建Adobe PDF其中emf图片公式马赛克化及文字缺失
软件版本 Word 2021 Visio 2019 Adobe Acrobat Pro 2020 问题描述 公式马赛克化,是指在Word中使用MathType编辑的公式,然后在Visio中使用图片(增强型图元文件)形式得到的粘贴对象,效果如下 文字缺失,是指Word导出→创建Adobe P…...
[matlab]matlab配置mingw64编译器
第一步:下载官方绿色版本mingw64编译器然后解压放到一个非中文空格路径下面 比如我mingw64-win是我随便改的文件名,然后添加环境变量,选择用户或者系统环境变量添加下面的变量 变量名: MW_MINGW64_LOC 变量值:自己的m…...
华为OD-非严格递增连续数字序列
题目描述 输入一个字符串仅包含大小写字母和数字 求字符串中包含的最长的非严格递增连续数字序列长度 比如: 12234属于非严格递增数字序列 输入描述 输入一个字符串仅包含大小写字母和数字 输出描述 输出字符串中包含的最长的非严格递增连续数字序列长度 示例…...
css滚动条样式这样修改下很漂亮
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>滚动条样式修改下很漂亮(不支持IE)</title> <style type"text/css"> * { margin: 0; padding: 0; } .box { width: 300px; height: 100px; margin…...
转置卷积的应用
目录 矩阵转置 一、转置卷积的背景 二、转置卷积的应用 三、转置卷积的区别 卷积 矩阵转置 矩阵的转置在信息处理中起到了重要的作用。在计算机科学领域,矩阵常用于表示图像、音频和视频等多媒体数据。当我们需要对这些数据进行处理时,常常需要进行…...
常见的移动端布局
流式布局(百分比布局) 使用百分比、相对单位(如 em、rem)等来设置元素的宽度,使页面元素根据视口大小的变化进行调整。这种方法可以实现基本的自适应效果,但可能在不同设备上显示不一致。 <!DOCTYPE ht…...
Typore 亲测有效(懂得都懂哈)
Typore 亲测从安装到使用,可以使用(具体是什么懂得都懂哈) 网盘下载地址:链接:https://pan.baidu.com/s/1w0UiS1szxnO9Lxz6sbXEKg?pwdqwe1 提取码:qwe1 第一步: 下载压缩包进行解压,解压过…...
Kyligence Copilot 登陆海外,斩获 Product Hunt 日榜 TOP 2
8月14日,AI 数智助理 Kyligence Copilot 在全球知名科技产品平台 Product Hunt 上线,其以出色的产品创新实力,在激烈的竞争中脱颖而出,仅仅在 24 小时内收获了超过 400 个投票和近 200 条支持评论,荣登当日产品榜排名第…...
【Docker】Docker 的基本概念和优势,基本命令及使用例子
Docker 是一种轻量级的容器化解决方案,能够快速地创建、部署和运行应用程序。以下是一些 Docker 的基本概念和优势: 基本概念: 1.镜像:一个 Docker 镜像是一个可执行的文件,其中包含了运行应用程序所需要的一切。 2.容…...
高并发内存池(回收)[4]
threadcache还给centralcache void ThreadCache::Deallocate(void* ptr, size_t size) {assert(ptr);assert(size < MAX_BYTES);// 找对映射的自由链表桶,对象插入进入size_t index SizeClass::Index(size);_freeLists[index].Push(ptr);// 当链表长度大于一次…...
分布式事务篇-2.4 Spring-Boot整合Seata
文章目录 前言一、pom jar导入:二、项目配置:2.1 配置 说明:2.1 .1 seata server 端:2.1 .2 seata client 端: 2.2 开启seata 对于数据源的代理:2.3 seata-client 的注册中心:2.4 seata-client 的配置中心:2.5 去掉手写的数据源代…...
718. 最长重复子数组
718. 最长重复子数组 原题链接:完成情况:题解:方法一:动态规划方法二:滑动窗口方法三:二分查找 哈希 原题链接: 718. 最长重复子数组 https://leetcode.cn/problems/maximum-length-of-repe…...
Mysql join加多条件与where的区别
最近在项目中遇到一个问题,感觉有点意思,在解决问题及查阅了相关资料后,打算写篇文章给朋友们分享一下。 问题现象: 问题是很常见的空指针问题,后端查询数据库数据,遍历进行相关业务处理时报空指针。通过…...
div滚动条自动滚动到底部
<div id"center"></div>// 滚动条到最底部scrollToBottom(){var box document.getElementById(center);this.$nextTick(() > {box.scrollTop box.scrollHeight})},...
【深度学习】实验02 鸢尾花数据集分析
文章目录 鸢尾花数据集分析决策树K-means 鸢尾花数据集分析 决策树 # 导入机器学习相关库 from sklearn import datasets from sklearn import treeimport matplotlib.pyplot as plt import numpy as np# Iris数据集是常用的分类实验数据集, # 由Fisher, 1936收集…...
AI大模型潮水中,医疗数字化加速「求解」
蝴蝶挥动翅膀,医疗行业每个角落开始连锁反应,曾经被忽视的问题也愈发明显。但与之对应的是,对数字化和AI大模型的价值认可,在中国医疗赛道也正在加速来临。 作者|斗斗 编辑|皮爷 出品|产业家 重庆市某地方人民医院…...
【安卓】自定义View实现画板涂鸦等功能
一、实现效果 二、代码 1、MainActivity.class package com.lsl.mydrawingboarddemo;import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat;import android.os.Bundle; import android.os.Handler; import android.view.View; impo…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
