news 2026/4/22 9:09:10

Java队列同步器的实现分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java队列同步器的实现分析

接下来将从实现角度分析同步器是如何完成线程同步的,主要包括:同步队列独占式同步状态获取与释放共享式同步状态获取与释放以及超时获取同步状态等同步器的核心数据结构与模板方法。

一、同步队列

同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态(其实就是锁)失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入同步队列,同时会阻塞当前线程当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。

同步队列中的节点(Node)用来保存获取同步状态失败的线程引用、等待状态以及前驱和后继节点,节点的属性类型与名称以及描述等信息。节点是构成同步队列的基础,同步器拥有首节点(head)和尾节点(tail),没有成功获取同步状态的线程将会成为节点加入该队列的尾部。

由上图可知,同步器包含了两个节点类型的引用,一个指向头节点,而另一个指向尾节点。试想一下,当一个线程成功地获取了同步状态(或者锁),其他线程将无法获取到同步状态,转而被构造成为节点并加入到同步队列中,而这个加入队列的过程必须要保证线程安全,因此同步器提供了一个基于CAS的设置尾节点的方法:compareAndSetTail(Node expect,Nodeupdate),它需要传递当前线程“认为”的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾节点建立关联。

同步队列遵循FIFO,首节点是获取同步状态成功的节点,首节点中的线程在释放同步状态时,将会唤醒它的后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点设置首节点是通过获取同步状态成功的线程来完成的,由于只有一个线程能够成功获取到同步状态,因此设置头节点的方法并不需要使用CAS来保证。

二、独占式同步状态获取与释放

通过调用同步器的acquire(int arg)方法可以获取同步状态,该方法对中断不敏感,也就是由于线程获取同步状态失败后进入同步队列中,后续对线程进行中断操作时,线程不会从同步队列中移出,同步器的acquire方法。

public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }

上述代码主要完成了同步状态获取节点构造加入同步队列以及在同步队列中自旋等待的相关工作,其主要逻辑是:首先调用自定义同步器实现的tryAcquire(int arg)方法,该方法保证线程安全的获取同步状态,如果同步状态获取失败,则构造同步节点(独占式Node.EXCLUSIVE,同一时刻只能有一个线程成功获取同步状态)并通过addWaiter(Node node)方法将该节点加入到同步队列的尾部,最后调用acquireQueued(Node node,int arg)方法,使得该节点以“死循环”的方式获取同步状态(死循环就是为了实现自旋)。如果获取不到则阻塞节点中的线程,而被阻塞线程的唤醒主要依靠前驱节点的出队或阻塞线程被中断来实现。

三、共享式同步状态获取与释放

共享式获取与独占式获取最主要的区别在于同一时刻能否有多个线程同时获取到同步状态。以文件的读写为例,如果一个程序在对文件进行读操作,那么这一时刻对于该文件的写操作均被阻塞,而读操作能够同时进行。写操作要求对资源的独占式访问,而读操作可以是共享式访问。

通过调用同步器的acquireShared(int arg)方法可以共享式地获取同步状态,在acquireShared(int arg)方法中,同步器调用tryAcquireShared(int arg)方法尝试获取同步状态,该方法返回值为int类型,当返回值大于等于0时,表示能够获取到同步状态。因此,在共享式获取的自旋过程中,成功获取到同步状态并退出自旋的条件就是该方法返回值大于等于0。

与独占式一样,共享式获取也需要释放同步状态,通过调用releaseShared(int arg)方法可以释放同步状态。该方法在释放同步状态之后,将会唤醒后续处于等待状态的节点。对于能够支持多个线程同时访问的并发组件(比如Semaphore),它和独占式主要区别在于tryReleaseShared(int arg)方法必须确保同步状态(或者资源数)线程安全释放,一般是通过循环和CAS来保证的,因为释放同步状态的操作会同时来自多个线程。

四、独占式超时获取同步状态

通过调用同步器的doAcquireNanos(int arg,long nanosTimeout)方法可以超时获取同步状态,即在指定的时间段内获取同步状态,如果获取到同步状态则返回true,否则,返回false。该方法提供了传统Java同步操作(比如synchronized关键字)所不具备的特性。

在分析该方法的实现前,先介绍一下响应中断的同步状态获取过程。在Java 5之前,当一个线程获取不到锁而被阻塞在synchronized之外时,对该线程进行中断操作,此时该线程的中断标志位会被修改,但线程依旧会阻塞在synchronized上,等待着获取锁。在Java 5中,同步器提供了acquireInterruptibly(int arg)方法,这个方法在等待获取同步状态时,如果当前线程被中断,会立刻返回,并抛出InterruptedException。超时获取同步状态过程可以被视作响应中断获取同步状态过程的“增强版”,doAcquireNanos(int arg,long nanosTimeout)方法在支持响应中断的基础上,增加了超时获取的特性。针对超时获取,主要需要计算出需要睡眠的时间间隔nanosTimeout,为了防止过早通知。nanosTimeout计算公式为:nanosTimeout-=now-lastTime,其中now为当前唤醒时间,lastTime为上次唤醒时间,如果nanosTimeout大于0则表示超时时间未到,需要继续睡眠nanosTimeout纳秒,反之,表示已经超时。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/21 11:24:07

金融产品介绍视频标准化:HeyGem统一品牌形象输出

金融产品介绍视频标准化:HeyGem统一品牌形象输出 在银行网点、手机App或客户经理的讲解屏前,你是否注意到——不同分支机构对同一款理财产品的解说,语速不一、重点各异,甚至口型与语音错位?这种“千人千面”的表达方式…

作者头像 李华
网站建设 2026/4/18 10:00:24

xhEditor word粘贴支持表格和列表

(扶了扶眼镜,敲着机械键盘开始码字) 各位老铁,作为山西网络安全专业的"码农",今天给大家表演一个"如何在预算99元内实现Word内容一键粘贴"的绝活。首先,咱们得给xhEditor这个老编辑器…

作者头像 李华
网站建设 2026/4/18 9:23:34

小目标检测在热红外域的挑战 _ - 无可见光条件下基于热特征的显著性建模 _ - - 夜间智能监控系统开发 如何训练无人机视角行人 _ 车辆检测与显著性分割的热红外数据集

面向无人机视角行人 / 车辆检测与显著性分割的热红外数据集 数据由 DJI M600Pro 无人机搭载 FLIR Vue Pro 热像仪在白天与夜间采集,专门解决“无人机视角下缺乏行人和车辆热红外公开数据集”的问题,并提供目标检测和显著性检测所需的像素级标注。 数…

作者头像 李华
网站建设 2026/4/21 10:09:12

爱沙尼亚语数字政府服务:公务员数字人讲解办事流程

爱沙尼亚语数字政府服务:公务员数字人讲解办事流程 在爱沙尼亚,一个不到140万人口的国家,99%的政务服务已实现在线办理。居民可以在几分钟内完成报税、注册公司甚至跨境业务申报——这一切都建立在“电子公民”(e-Residency&#…

作者头像 李华
网站建设 2026/4/20 18:09:26

马耳他语地中海饮食推广:厨师数字人推荐健康食谱

马耳他语地中海饮食推广:厨师数字人推荐健康食谱 —— 基于 HeyGem 数字人视频生成系统的技术实现 在马耳他,一种被联合国教科文组织列为非物质文化遗产的地中海饮食文化正面临传播困境:尽管它以橄榄油、新鲜蔬果和鱼类为核心,被誉…

作者头像 李华
网站建设 2026/4/19 2:13:03

如何用C#实现跨平台请求拦截与调试?90%开发者忽略的关键细节

第一章:C#跨平台请求拦截的现状与挑战在现代软件开发中,C# 作为一门强大的面向对象语言,已通过 .NET Core 和 .NET 5 实现了真正的跨平台能力。随着应用架构向微服务和分布式系统演进,对 HTTP 请求进行拦截、监控和修改的需求日益…

作者头像 李华