AQS同步队列和等待队列的同步机制

news/2024/5/20 8:11:09 标签: java, 并发, JUC

理解AQS必须要理解同步队列和等待队列之间的同步机制,简单来说流程是:
获取锁失败的线程进入同步队列,成功的占用锁,占锁线程调用await方法进入条件等待队列,其他占锁线程调用signal方法,条件等待队列线程进入同步队列排队。
举个例子:
去银行办理业务,需要排队、在窗口办理、费时间的有专员带你去小屋办理。三者之间的关系如下图:

  • 持有锁线程:相当于银行窗口一次只能一人坐那
  • 同步队列:相当于去银行拿了号在座位上排队
  • 条件等待队列:相当于复杂业务,经理把你从窗口叫走去小屋办理
    在这里插入图片描述
    这天你去银行办理业务(也就是要获取锁),只有一个窗口,如果你是第一个去的,则占用窗口办理即可。
    如果窗口有人,且排队人较多,你就先拿个号,排在队伍末尾依次等待叫号。
    在这里插入图片描述
    当下一个就轮到你时,窗口人走了,会叫你一声,这时候你就持有锁,在窗口位置占着。
    在这里插入图片描述
    办理过程中,专员发现你的业务过于费时间,且不需要你一直在窗口等着,阻塞了后面排队人的进度。这时专员会通知你,让你去等待区域等着(相当于调用了await方法)。你就进入了等待队列。
    在这里插入图片描述
    等你的业务处理完了,窗口的专员会通知你(signal方法),但排队区已经有人排队等了很长时间,这时候你要是直接插到窗口不合适,所以需要再次到到队伍末尾排队,即进入同步队列。再次等待依次叫号。
    上面通过一个简单的例子讲述了同步队列和条件等待队列的流转过程。下面从理论和代码层面看看如果理解。

1 同步队列和等待队列简述

AQS维护的队列是当前等待资源的队列,即获取锁失败的线程。当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
在这里插入图片描述

每个Condition维护着一个队列,该队列的作用是维护一个等待singal信号的队列。
在这里插入图片描述

java">/AQS中的Node属性
    static final class Node {
        ...
        volatile int waitStatus;//等待状态
        volatile Node prev;//前驱节点
        volatile Node next;//后驱节点
        volatile Thread thread;//获取同步状态的线程,当前执行线程
        Node nextWaiter;//等待队列中的后继节点
        ...
    }

从上述Node结构可以看出,其实同步队列和等待队列使用的是同一个Node类型AbstractQueuedSynchronizer.Node。

2 同步队列和等待队列区别与协同机制

从简述中我们可知,同步队列和等待队列的作用是不同的。最重要的区别是:每个线程只能存在于同步队列或等待队列中的一个。

下面我们举一个具体的例子来说明同步队列和等待队列之间的区别与协同工作:

  1. 同步队列的初始状态为下图,同步队列中包含线程A(节点A)和线程B(节点B),线程调用reentrantLock.lock()时,线程被加入到AQS同步队列中
    在这里插入图片描述

  2. 线程A(节点A)调用condition.await()方法时,线程A(节点A)从AQS同步队列中被移除,对应操作是锁的释放; 线程A(节点A)接着被加入到Condition等待队列,因为线程需要singal信号。

  3. 线程B(节点B)由于线程A(节点A)释放锁被唤醒,判断成为同步队列头结点且同步状态为0可以获取锁;线程B(节点B)获取锁。
    在这里插入图片描述

  4. 线程B(节点B)调用singal()方法,Condition等待队列中有一个节点A,把它取出来加入到AQS同步队列中。这时候线程A(节点A)并没有被唤醒。
    在这里插入图片描述

  5. 线程B(节点B)singal方法执行完毕,调用reentrantLock.unLock()方法释放锁。线程A(节点A)成为AQS首节点并且同步状态可获取,线程A(节点A)被唤醒,继续执行。

  6. AQS从头到尾顺序唤醒线程,直到等待队列中的线程被执行完毕结束。

可以参考下原作者的文章,不要放过评论区部分的补充:http://ifeve.com/understand-condition/


http://www.niftyadmin.cn/n/5013810.html

相关文章

推荐系统(概要+召回)

推荐系统 一、概要 1.基本概念 用户行为:点击、点赞、收藏、转发消费指标:点击率 (click rate)、交互率 (engagement rate)北极星指标:用户规模、消费、发布实验流程:离线实验、AB测试、推全 2.推荐系统的链路 召回&#xff…

12 Python使用xml

概述 在上一节,我们介绍了Python的正则表达式,包括:正则表达式的定义、正则表达式的语法、re.search函数、re.match函数、re.findall函数、re.sub函数、re.compile函数、re.finditer函数、re.split函数等内容。在这一节,我们将介绍…

202 - Repeating Decimals (UVA)

题目链接如下&#xff1a; Online Judge 我的代码如下&#xff08;start表示开始循环的位数&#xff0c;pos表示在小数点后的位数&#xff0c;cycle表示循环的长度&#xff0c;map表示从被除数映射到当时的pos位数&#xff09;&#xff1a; #include <cstdio> #includ…

apache-activemq-5.17.1一键安装安装

下载 安装 双击InstallService.bat脚本 查看是否安装完成

滚珠螺杆螺母的加工方法

螺母就是螺帽&#xff0c;与螺栓或螺杆拧在一起用来起紧固作用的零件&#xff0c;螺母的用途十分广泛&#xff0c;那么它的加工方法&#xff0c;你了解吗&#xff1f;接下来&#xff0c;我们一起来看一下。 1、车削&#xff1a;最早使用的加工方法&#xff0c;加工螺母的工艺路…

vue3+ts项目打包后的本地访问

注意&#xff1a;打包之后不可直接点击html访问&#xff0c;需要给项目安装本地服务&#xff01; 1、安装servenpm i -g serve 2、打包项目npm run build 生成dist文件夹 3、本地访问serve dist 运行service dist之后的控制台 可复制下方的地址运行打包后的项目&#xff0c;运行…

Python给列表提前设置好大小的方法

目录 前言 方法一 方法二 总结 前言 在Python中&#xff0c;列表是一种动态的数据结构&#xff0c;可以根据需要动态增加或减少其大小。因此&#xff0c;通常不需要提前设置列表的大小。但是&#xff0c;如果你确实需要在列表创建时就指定其大小&#xff0c;可以使用以下两种…

HarmonyOS开发:走进静态共享包的依赖与使用

前言 在上一篇&#xff0c;我们进行了动态共享包的开发和使用&#xff0c;由于动态共享包有一定的局限性&#xff0c;比如&#xff0c;调用共享包资源还得要通过工具类进行调用&#xff0c;再比如仅用于应用内部代码、资源的共享&#xff0c;如果我想要开源&#xff0c;以远程依…