news 2026/4/7 15:09:44

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/4 21:22:43

语音克隆结合HeyGem:打造专属声线+数字人完整解决方案

语音克隆结合HeyGem:打造专属声线数字人完整解决方案 在虚拟主播24小时不间断直播、企业宣传视频批量生成、在线课程快速迭代的今天,内容创作的“工业化”需求正以前所未有的速度增长。而传统数字人制作依赖专业演员录制、后期逐帧调口型、多团队协作的工…

作者头像 李华
网站建设 2026/4/5 11:21:18

阿拉伯语宗教诵读:清真寺伊玛目数字人示范正确发音

阿拉伯语宗教诵读:清真寺伊玛目数字人示范正确发音 在伊斯兰教育中,准确掌握《古兰经》的诵读规则(Tajweed)是一项极其严肃且精细的任务。一个音节的误读,哪怕只是轻微的元音拖长或停顿不当,都可能改变经文…

作者头像 李华
网站建设 2026/3/28 7:43:19

儿童绘本故事动画化:HeyGem助力亲子教育内容创作

儿童绘本故事动画化:HeyGem助力亲子教育内容创作 在幼儿园的睡前故事时间,老师用温柔的声音讲述《小熊找朋友》,孩子们睁大眼睛听得入神。可如果这位“老师”是一个会动嘴、有表情的数字人,而同一个故事还能由“穿围裙的女老师”“…

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

小说有声剧升级:HeyGem为角色赋予面部表情与口型

小说有声剧的视觉革命:HeyGem如何让角色“开口说话” 在音频内容泛滥的今天,用户早已不再满足于“只听声音”。无论是网络小说演播、儿童故事讲解,还是知识类短视频,听众越来越期待看到与声音同步的“人物表现”——一个会动嘴唇、…

作者头像 李华
网站建设 2026/3/22 20:07:37

LUT调色包下载后如何应用?HeyGem输出视频后期美化方案

LUT调色包下载后如何应用?HeyGem输出视频后期美化方案 在AI生成内容(AIGC)席卷短视频、在线教育和虚拟主播的今天,数字人技术已不再是实验室里的概念——它正被大量用于企业宣传、课程录制甚至新闻播报。像HeyGem这样的语音驱动数…

作者头像 李华
网站建设 2026/4/3 4:45:49

俄语新闻听力训练:主播数字人播报今日要闻

俄语新闻听力训练:主播数字人播报今日要闻 在语言教学领域,尤其是外语听力训练中,内容的时效性与多样性长期面临挑战。教师们常常陷入两难:想用真实新闻材料提升学生语感,却受限于版权、发音标准和制作成本&#xff1b…

作者头像 李华