并发编程:AQS(上)

news2024/11/15 17:17:25

一、AQS 是什么?

AQS 的全称为 AbstractQueuedSynchronizer ,翻译过来的意思就是抽象队列同步器。这个类在 java.util.concurrent.locks 包下面。

 AQS 就是一个抽象类,主要用来构建锁和同步器。

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
}

AQS 为构建锁和同步器提供了一些通用功能的实现,因此,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 ReentrantLockSemaphore,其他的诸如 ReentrantReadWriteLockSynchronousQueue等等皆是基于 AQS 的。

二、AQS 的原理是什么?

AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁 实现的,即将暂时获取不到锁的线程加入到队列中。

CLH(Craig,Landin,and Hagersten) 队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。在 CLH 同步队列中,一个节点表示一个线程,它保存着线程的引用(thread)、 当前节点在队列中的状态(waitStatus)、前驱节点(prev)、后继节点(next)。

CLH 队列结构如下图所示:

AQS(AbstractQueuedSynchronizer)的核心原理图如下:

 

AQS 使用 int 成员变量 state 表示同步状态,通过内置的 线程等待队列 来完成获取资源线程的排队工作。

state 变量由 volatile 修饰,用于展示当前临界资源的获锁情况。

// 共享变量,使用volatile修饰保证线程可见性
private volatile int state;

 另外,状态信息 state 可以通过 protected 类型的getState()setState()compareAndSetState() 进行操作。并且,这几个方法都是 final 修饰的,在子类中无法被重写。

//返回同步状态的当前值
protected final int getState() {
     return state;
}
 // 设置同步状态的值
protected final void setState(int newState) {
     state = newState;
}
//原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
      return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

ReentrantLock 为例,state 初始值为 0,表示未锁定状态。A 线程 lock() 时,会调用 tryAcquire() 独占该锁并将 state+1 。此后,其他线程再 tryAcquire() 时就会失败,直到 A 线程 unlock()state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A 线程自己是可以重复获取此锁的(state 会累加),这就是可重入的概念。但要注意,获取多少次就要释放多少次,这样才能保证 state 是能回到零态的。

再以 CountDownLatch 以例,任务分为 N 个子线程去执行,state 也初始化为 N(注意 N 要与线程个数一致)。这 N 个子线程是并行执行的,每个子线程执行完后countDown() 一次,state 会 CAS(Compare and Swap) 减 1。等到所有子线程都执行完后(即 state=0 ),会 unpark() 主调用线程,然后主调用线程就会从 await() 函数返回,继续后余动作。

三、Semaphore 有什么用?

synchronizedReentrantLock 都是一次只允许一个线程访问某个资源,而Semaphore(信号量)可以用来控制同时访问特定资源的线程数量。

Semaphore 的使用简单,我们这里假设有 N(N>5) 个线程来获取 Semaphore 中的共享资源,下面的代码表示同一时刻 N 个线程中只有 5 个线程能获取到共享资源,其他线程都会阻塞,只有获取到共享资源的线程才能执行。等到有线程释放了共享资源,其他阻塞的线程才能获取到。

// 初始共享资源数量
final Semaphore semaphore = new Semaphore(5);
// 获取1个许可
semaphore.acquire();
// 释放1个许可
semaphore. Release();

当初始的资源个数为 1 的时候,Semaphore 退化为排他锁。

Semaphore 有两种模式:。

  • 公平模式: 调用 acquire() 方法的顺序就是获取许可证的顺序,遵循 FIFO;
  • 非公平模式: 抢占式的。

Semaphore 对应的两个构造方法如下:

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

这两个构造方法,都必须提供许可的数量,第二个构造方法可以指定是公平模式还是非公平模式,默认非公平模式。

Semaphore 通常用于那些资源有明确访问数量限制的场景比如限流(仅限于单机模式,实际项目中推荐使用 Redis +Lua 来做限流)。

四、Semaphore 的原理是什么?

Semaphore 是共享锁的一种实现,它默认构造 AQS 的 state 值为 permits,你可以将 permits 的值理解为许可证的数量,只有拿到许可证的线程才能执行。

调用semaphore.acquire() ,线程尝试获取许可证,如果 state >= 0 的话,则表示可以获取成功。如果获取成功的话,使用 CAS 操作去修改 state 的值 state=state-1。如果 state<0 的话,则表示许可证数量不足。此时会创建一个 Node 节点加入阻塞队列,挂起当前线程。

/**
 *  获取1个许可证
 */
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
/**
 * 共享模式下获取许可证,获取成功则返回,失败则加入阻塞队列,挂起线程
 */
public final void acquireSharedInterruptibly(int arg)
    throws InterruptedException {
    if (Thread.interrupted())
      throw new InterruptedException();
        // 尝试获取许可证,arg为获取许可证个数,当可用许可证数减当前获取的许可证数结果小于0,则创建一个节点加入阻塞队列,挂起当前线程。
    if (tryAcquireShared(arg) < 0)
      doAcquireSharedInterruptibly(arg);
}

调用semaphore.release(); ,线程尝试释放许可证,并使用 CAS 操作去修改 state 的值 state=state+1。释放许可证成功之后,同时会唤醒同步队列中的一个线程。被唤醒的线程会重新尝试去修改 state 的值 state=state-1 ,如果 state>=0 则获取令牌成功,否则重新进入阻塞队列,挂起线程。

// 释放一个许可证
public void release() {
    sync.releaseShared(1);
}

// 释放共享锁,同时会唤醒同步队列中的一个线程。
public final boolean releaseShared(int arg) {
    //释放共享锁
    if (tryReleaseShared(arg)) {
      //唤醒同步队列中的一个线程
      doReleaseShared();
      return true;
    }
    return false;
}

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

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

相关文章

怎么抓住威士忌蓝海市场?

​前些天和朋友吃饭&#xff0c;聊起来威士忌。 朋友说&#xff0c;这个威士忌啊&#xff0c;最近几年增值特别快&#xff0c;甚至超过了黄金、名表、艺术品、红酒这些经典的投资品类。而且存放得越久&#xff0c;升值的幅度就越大。 再加上&#xff0c;中国的威士忌市场现在…

3GPP NTN定义了哪些band?

卫星频段主要包括以下几种&#xff1a; L波段(1–2 GHz)&#xff1a;主要用于移动卫星通信、导航系统和无线电测绘。例如&#xff0c;全球定位系统 (GPS) 运营商以及卫星移动电话&#xff0c;如 Iridium和 Inmarsat提供海上、陆地和空中通信。 S波段(2–4 GHz)&#xff1a;广泛…

【C++ Primer Plus习题】13.3

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include <string> #inc…

[Unity]如何让选择区域(照射范围)内的物体实时高亮

前言 最近希望在Unity里实现这样一个功能&#xff1a; 黄色半透明部分是一个手电筒&#xff0c;我希望手电筒范围内的小球会高亮轮廓&#xff0c;且手电筒移出去后小球会恢复原来的样子&#xff1a; 一开始我把整个问题想的太复杂了&#xff0c;想着在Update()函数里暴力检…

使用Python自动抓取亚马逊网站商品信息

全量数据抓取不现实&#xff0c;但可以自动化、小批量采集亚马逊数据&#xff0c;现在可用的工具也非常多&#xff0c;包括Python以及一些专门的爬虫软件&#xff0c;我用过几个比较好入手的&#xff0c;像web scraper、八爪鱼、亮数据。 比如亮数据爬虫&#xff0c;它提供数据…

408:强化笔记|王道|DS|OS|CO|计网

目录 DS 数据结构算法题一、快速排序二、二路归并排序三、链表(2.3课后习题)四、二叉树五、图应用题 OS 操作系统第二章 进程与线程零、大观一、PV操作 第三章 内存管理一、内存管理大题 CO 计算机组成原理第三章 存储系统一、Cache大题二、TLB大题 第二章 数据的表示和运算第四…

Nginx之日志切割,正反代理,HTTPS配置

1 nginx日志切割 1.1 日志配置 在./configure --prefixpath指定的path中切换进去&#xff0c;找到log文件夹&#xff0c;进去后找到都是对应的日志文件 其中的nginx.pid是当前nginx的进程号&#xff0c;当使用ps -ef | grep nginx获得就是这个nginx.pid的值 在nginx.conf中…

安全测试:全面指南!

引言 随着互联网技术的快速发展&#xff0c;网络安全威胁日益增多。企业不仅需要保护其数据资产免受外部攻击&#xff0c;还需要确保内部系统的安全性。安全测试作为软件开发生命周期中的关键环节&#xff0c;旨在识别潜在的安全漏洞和风险点&#xff0c;从而提高产品的整体安…

Return arguments from function calling with OpenAI API when streaming?

题意&#xff1a;在使用OpenAI API进行流式传输时&#xff0c;如何返回函数调用的参数&#xff1f; 问题背景&#xff1a; Ive made a simple OpenAI API example with function calling. Im only using function calling to format the response, Im not calling multiple fu…

沪农商行半年报“双正增”:分红率提升,业绩增速却放缓明显

《港湾商业观察》施子夫 王璐 虽然上半年业绩增速上有所放缓&#xff0c;但沪农商行(601825.SH)33%的分红率至少对投资者来说无疑是个好消息。 今年上半年&#xff08;报告期内&#xff09;&#xff0c;沪农商行实现营业收入139.17亿元&#xff0c;同比增长0.23%&#xff1b…

TikTok运营需要的独立IP如何获取?

TikTok作为当下炙手可热的社交媒体平台&#xff0c;吸引了众多个人创作者和企业进驻。在进行TikTok运营时&#xff0c;许多经验丰富的用户都倾向于选择独立IP。那么&#xff0c;TikTok运营为什么需要独立IP&#xff1f;又该如何获取呢&#xff1f;本文将详细为您解答这些问题。…

HivisionIDPhotos

在服务器Ubuntu22.04系统下&#xff0c;HivisionIDPhotos的部署 一、安装环境&#xff1a;ubuntu基本环境配置1.更新包列表&#xff1a;2. 安装GPU驱动程序3.查看显卡信息4.下载并安装 CUDA 12.3 二、安装miniconda环境1. 下载miniconda32. 安装miniconda33. 打开用户环境编辑页…

英文站外链如何建设?

在建设英文站外链时&#xff0c;关键在于高质量和低质量&#xff08;数量多&#xff09;链接的合理搭配。几百条外链想提升网站是不现实的&#xff0c;所以数量是绝对的&#xff0c;当然&#xff0c;更重要的是&#xff0c;这些外链能够为你的网站SEO带来实际的提升 GPB外链就是…

Vue学习:计算属性Computed

计算属性可以实时监听 data节点中数据的变化&#xff0c;并 return 一个计算后的新值&#xff0c;供组件渲染 DOM 时使用&#xff0c;计算属性需要以 function 函数的形式声明到组件的 computed 节点中。 计算属性的使用注意点&#xff1a; &#xff08;1&#xff09;计算属性…

PDF转换器竟能如此简单?这款工具让无数人为之疯狂,你试过吗?

现在大家都用电脑办公&#xff0c;PDF文件因为不管在哪儿都能打开&#xff0c;而且内容不会乱&#xff0c;所以特别受欢迎。但有时候也挺麻烦的&#xff0c;比如你得改改里面的内容或者转成别的格式。到了2024年&#xff0c;如果你还被PDF文件搞得头疼&#xff0c;那就试试这四…

Cross Explosion

考查找。 线性查找上下左右四个方向看到的第一个的墙的位置复杂度O(n^2)&#xff0c;在Q取到2e5的情况下直接爆掉。 这里应使用二分查找&#xff0c;对某一行、列进行二分&#xff0c;使用set实现墙的位置存储。 #include<bits/stdc.h> using namespace std; #define…

IBM中国研发部裁员:全球化背景下的IT产业变局与应对之道

裁员风波中的思考与机遇 前言了解霍尼韦尔的“东方服务东方”施耐德电气的“中国中心”战略对比与分析 中国信息技术(IT)行业展现出蓬勃发展的前景**政府支持与政策导向****技术创新与应用****市场规模与需求****人才培养与就业**国际化与开放合作总结 前言 如何看待IBM中国研发…

C#--CM+Fody+HCWPF开发组合

CM&#xff1a;Caliburn.Micro(简称CM)一经推出便备受推崇&#xff0c;作为一款MVVM开发模式的经典框架&#xff0c;越来越多的受到wpf开发者的青睐.我们看一下官方的描述&#xff1a;Caliburn是一个为Xaml平台设计的小型但功能强大的框架。Micro实现了各种UI模式&#xff0c;用…

大学生租房平台:SpringBoot技术实现详解

第3章 系统分析 面对即将开发的系统&#xff0c;进行提前的分析是必要的。这也是开发流程中必须有的环节。通常分析系统期间&#xff0c;主要涉及的内容包括系统开发可行性问题&#xff0c;对系统功能和性能的分析等问题。 3.1 可行性分析 在正式对需要建设的项目进行投资前&am…

Gmtracker_深度学习驱动的图匹配多目标跟踪项目启动与算法流程

Gmtracker深度学习驱动的图匹配多目标跟踪项目启动与算法流程 说明&#xff1a;对于Gmtracker多目标跟踪算法中涉及到的QP或者是QAP等一些有关图匹配的问题&#xff0c;不做过多的说明只提供源代码中通过图网络的具体实现细节。 对于配置环境时产生的报错的具体信息&#xff0c…