AQS的同步队列和条件队列原理

news2024/9/19 10:34:44

文章目录

  • 二者区别
  • 实现原理
    • 同步队列
      • 原码-内部类Node
      • 源码-获取锁
      • 源码-释放锁
    • 条件队列
      • 原码-内部类Node
      • 源码-等待
      • 源码-唤醒
  • Demo-比较典型的条件队列使用场景

二者区别

首先,AQS中的 同步队列 和 条件队列 是两种不同队列:

  • 目的不同:同步队列主要用于实现锁机制(也就是锁的获取和释放),而条件队列用于实现条件变量,条件变量是并发编程中一种用于线程间通信的机制,它允许一个或多个线程在特定条件成立之前等待并释放相关的锁,直到其他线程改变了条件并显式的唤醒等待在该条件中的线程。比较典型的一个条件队列使用场景就是 ReentrantLock 的 Condition。
  • 使用方式不同:同步队列是AQS自动管理的,开发者通常不需要直接与之交互;而条件队列是通过Condition接口暴露给开发者的,需要显示地调用 等待await()方法 和 通知signal()/signalAll()方法
  • 队列类型不同:虽然它们都是队列结构,但同步队列是所有基于AQS同步器共享的,每个同步器实例只有一个同步队列;但条件队列是每个 Condition实例特有的,一个同步器可以有多个 Condition对象,因此也就有多个条件队列。

实现原理

在这里插入图片描述

同步队列

同步队列的实现原理比较简单,AQS中同步队列是一个FIFO队列,节点类型为AQS的内部类Node。Node有两个指针,prev和next,分别指向前置节点和后置节点,一个个Node节点组成了一个双向链表。

  1. 当一个线程尝试获取锁失败时,它会被封装成一个Node节点加入到队列尾部。
  2. 这个节点会处于等待状态,直到锁被其他线程释放。
  3. 当锁被释放时,队列的头节点(持有锁的线程)会通知其后继节点(如果存在的话),后继节点尝试获取锁。这个过程会一直持续,直到队列为空。

原码-内部类Node

static final class Node {
   
     	// 前置节点和后置节点构成双向链表
        volatile Node prev;
        volatile Node next;
        // 线程本身
        volatile Thread thread;
        // 状态信息,标识节点在同步队列中的等待状态
        volatile int waitStatus;
}

源码-获取锁

public final void acquire(int arg) {
   
	// 尝试获取锁失败之后,将线程封装成一个Node节点加入队列
	if (!tryAcquire(arg) &&
			acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
		selfInterrupt();
	}
}

private Node addWaiter(Node mode) {
   
	Node node = new Node(Thread.currentThread(), mode);
	// 首先尝试直接在尾部插入节点
	Node pred = tail;
	if (pred != null) {
   
	    node.prev = pred;
	    if (compareAndSetTail(pred, node)) {
   
	        pred.next = node;
	        return node;
	    }
	}
	// 插入失败时,进入完整的入队操作
	enq(node);
	return node;
}

private Node enq(final Node node) {
   
    for (;;) {
   
        Node t = tail;
        if (t == null) {
    // 队列为空,初始化(第一个节点为虚节点,也叫哨兵节点,并不存储任何信息,只是占位。真正的第一个有数据的节点,是从第二个节点开始的。)
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
   
            node.prev = t;
            if (compareAndSetTail(t, node)) {
   
                t.next = node;
                return t;
            }
        }
    }
}

acquireQueued() 中方法链路太长,这里拆开讲解。

// 在加入队列排队的同时尝试抢夺资源
final boolean acquireQueued(final Node node, int arg) {
   
    boolean failed = true;
    try {
   
        boolean interrupted = false;
      	// 自旋
        for (;;) {
   
          	// 获得当前节点的前置节点,判断当前节点是否是头节点。若是则进行抢占锁
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
   
              	// 若抢占成功,则设置当前节点为头节点
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
          	// 修改Node节点状态,使其线程等待。等候唤醒
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
              	// 线程被唤醒,进行自旋判断抢占锁
                interrupted = t

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4.3.2 C++ 平面拟合的实现

4.3.2 C 平面拟合的实现 参考教程: gaoxiang12/slam_in_autonomous_driving: 《自动驾驶中的SLAM技术》对应开源代码 (github.com) Eigen打印输出_打印eigen矩阵-CSDN博客 1. 编写 Plane fitting 1.1 创建文件夹 通过终端创建一个名为Plane_fitting的文件夹以保…

Cookie和Session的区别(详细讲解)

Session 和 Cookie 的区别 在网络通信过程中,使用的是HTTP协议,它是一种无状态协议,比如,在登录一个网站时,登录成功后,之后在访问这个网站的其他页面时,都需要重新登录,无法识别出…

[240812] X-CMD 发布 v0.4.5:更新 gtb、cd、chat、hashdir 模块功能

目录 📃Changelog✨ gtb✨ cd✨ chat✨ hashdir 📃Changelog ✨ gtb 调整了 fzf 预览窗口中书籍文本的显示效果,通过识别文本中的特殊字符、日期、章节标题等信息,为其赋予不同的颜色。 ✨ cd cd 模块新增功能:在找…

网络编程day03 20240813

一、相关练习 1、通过w(红色臂角度增大)s(红色臂角度减小)d(蓝色臂角度增大)a(蓝色臂角度减小)按键控制机械臂 机械臂需要发送16进制数,共5个字节,协议如下: 0xff 0x0…

数据库核心技术:存储与索引概览

文章目录 存储与索引技术概览存储结构索引技术 MySQL存储结构索引技术事务与锁优势 PostgreSQL存储结构索引技术事务与锁优势 Oracle存储结构索引技术事务与锁优势 SQL Server存储结构索引技术事务与锁优势 选型考量存储结构索引技术事务与锁的支持综合因素未来趋势 结语 数据库…

【微信小程序】网络数据请求

1. 小程序中网络数据请求的限制 2. 配置 request 合法域名 3. 发起 GET 请求 调用微信小程序提供的 wx.request() 方法,可以发起 GET 数据请求,示例代码如下: 4. 发起 POST 请求 调用微信小程序提供的 wx.request() 方法,可以发起 POST 数据请求,示例代码如下: 5. …

今日头条的账号id在哪里看(网页版)

今日头条的账号id在哪里看(网页版) 1.https://mp.toutiao.com/profile_v4/index2.登录今日头条账号3.设置->头条号ID 1.https://mp.toutiao.com/profile_v4/index 2.登录今日头条账号 3.设置->头条号ID 打开下方链接: https://mp.to…

进程的执行与结束

一、文件的读写 1、fork之前open 子进程会继承父进程已打开的文件相关信息 所以,此时父子进程会影响一个offset值 2、fork之后open 父子进程各自有各自的打开文件的信息,相互之间不会有影响。 二、 进程的执行 //进程运行时,典型场景 1…

量化策略开发步骤系列(3)关键投资组合指标

量化策略开发步骤系列(3)关键投资组合指标 投资组合指标波动性夏普比率最大回撤赢/输百分比每笔交易的平均利润/亏损每期交易次数风险价值(VaR) 这是量化交易系列文章的第二系列——量化策略开发步骤,第一系列请参考专…

Prometheus+Grafana-3-Nginx监控-Redis监控

一、监控Nginx 1.Nginx需要开启stub_status 这里我的nginx容器名为mynignx,进入容器查看。 docker exec -it mynginx bash #进入容器 nginx -v 2>&1 | grep -o with-http_stub_status_module #查看 修改nginx.conf ...location /stub_status {stub_stat…

华为od统一考试B卷【AI面板识别】python实现

思路 n int(input())class Light:def __init__(self, id, x1, y1, x2, y2):self.id idself.x1 x1self.y1 y1self.x2 x2self.y2 y2self.height y2 - y1def get_lights_info(n):lights []for _ in range(n):id, x1, y1, x2, y2 map(int, input().strip().split())lights…

量产工具——复习及改进(后附百问网课程视频链接)

目录 一、函数的使用 1.显示系统 1.1 mmap函数 2.输入系统 2.1 ts_setup()函数 2.2 ts_read()函数 2.3 socket()函数 2.4 bind()函数 2.5 recvfrom()函数 2.6 inet_aton()函数 2.7 sendto()函数 2.8 pthread_create()函数 2.9 pthread_cond_signal()函数 2.10 pthre…

思科路由器的基本配置1

#路由技术基础# #路由器的基本配置1# #1调整超级终端的参数 #2退出配置向导,输入“NO”即可进入正常配置方式 #3路由器的模式切换 Router> !进入用户模式 Router>enable !进入特权模…

opencv-python实战项目一:获取鼠标框选区域的颜色

文章目录 一:简介二:框选区域选择颜色方案三、算法实现步骤3.1 按鼠标事件截取图像3.2将图像模糊后转化为hsv并求均值3.3 判断hsv处于何种颜色 四:整体代码实现五,效果: 一:简介 在计算机视觉领域,颜色检测…

19-ESP32-C3加大固件储存区

1默认编译情况。 2、改flash4M。ESP-IDF Partition Table Editor修改。 3、设置输入Partition Table 改自定义.CSV。保存。 4、查看命令输入Partition Table Editor打开-分区表编辑器UI。按图片增加。 nvs,data,nvs,0x9000,0x6000,, phy_init,data,phy,0xF000,0x1000,, factory…

计算机毕业设计Hadoop+Hive居民用电量分析 居民用电量可视化 电量爬虫 机器学习 深度学习 大数据毕业设计 Spark

《Hadoop居民用电量分析》开题报告 一、研究背景与意义 能源问题在全球范围内一直是热点议题,尤其是随着居民生活水平的提高和城市化进程的加快,居民用电量急剧增长,对电力系统的稳定运行和能源管理提出了更高要求。如何科学合理地管理和分…

Hive3:识别内部表、外部表及相互转换

一、识别方法 查看内部表信息 desc formatted stu;查看外部表信息 desc formatted test_ext1;通过Table Type对应的值,我们可以区分外部表和内部表。 二、相互转换 内部表转外部表 alter table stu set tblproperties(EXTERNALTRUE);外部表转内部表 alter ta…

PCIe学习笔记(21)

读请求的数据返回(Data Return for Read Requests) •针对内存读取请求的单个完成可能提供少于请求的全部数据量,只要对于给定请求的所有完成在组合起来时返回了读取请求中请求的数据量。 ◦不同请求的完成不能合并。 ◦I/O和Configuratio…

Qt QCustomPlot 图形库详解

文章目录 原文1. 下载qcustomplot.h与qcustomplot.cpp后,将代码文件拷贝到本地工程,并添加到工程项目2. 看到文件后就是添加成功了3. 在界面中拖拽一个Widget控件,选中并右键选中“提升为”,将原来的Widget控件已成为一个带坐标的 CustomPlot 控件4. 添加printsupport原文 …

【Ai学习】一个技巧,解决99%Comfyui报错!

前言 comfyui以极高灵活度及节点化工作流,深受AI绘画者追捧,每当新的模型开源,comfyui都是最先进行适配。 comfyui高度兼容性及灵活性带来丰富强大的扩展(插件)生态,同时也带来一系列插件安装的问题&…