Java并发面试题:(八)AQS原理和Semaphore、CountdownLatch、CyclicBarrier类

news/2024/7/5 5:35:43

什么是AQS

通过维护一个共享资源状态( Volatile Int State )和一个先进先出( FIFO )的线程等待队列来实现一个多线程访问共享资源的同步框架。

AQS队列同步器(AbstractQueuedSynchronizer),是用来构建锁或者其他同步组件的基础框架。
它使用了一个int的成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
同步器的主要使用方式是继承,子类通过基础同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了对同步状态进行更改,这时需要使用同步器提供的三个方法(getState()、setState(int newState)和compareAndSetState(int expect,int update)来操作,因为他们能够保证状态的改变是安全的。

AQS内部维护的属性 volatile int state,state表示锁同步状态[独占锁表示是否占有,重入时自增。共享锁一般有其他count表示重入。
state的三种访问方式:getState() 直接从内存中读取同步器状态值
setState() 设置同步器状态值(一般是释放锁时使用)
compareAndSetState() 尝试修改锁状态(抢占锁时使用)

AQS定义两种队列
同步等待队列:用于维护获取锁失败的入队线程
条件等待队列:调用await()方法释放锁后,加入条件队列,等待条件唤醒再次争抢锁

AQS原理

AQS 为每个共享资源都设置一个共享资源锁,线程在需要访问共享资源时首先需要获取共享资源锁,如果获取到了共享资源锁,便可以在当前线程中使用该共享资源,如果获取不到,则将该线程放入线程等待队列,等待下一次资源调度。许多同步类的实现都依赖于AQS ,例如常用的 ReentrantLock、Semaphore、CountDownLatch。

AQS 维护了 volatile int state类型的变量,用于表示当前的同步状态。volatile虽然不能保证操作的原子性,但是能保证当前变量state的可见性。
state的访问方式有三种: getState()、setState()和 compareAndSetState(),均是原子操作,其中,compareAndSetState的实现依赖于 Unsafe的compareAndSwaplnt() 具体的。

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

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

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

AQS共享资源的方式:独占式和共享式

AQS 定义了两种资源共享方式 :独占式 (Exclusive)和共享式(Share)
独占式:只有一个线程能执行,具体的 Java 实现有 ReentrantLock。
共享式:多个线程可同时执行,具体的 Java 实现有 Semaphore和CountDownLatch。

AQS的组件

CountDownLatch,可以实现某一些线程,等待其他线程执行完之后,才可以执行
CyclicBarrier,实现了多个线程间的相互等待,直到大家等待的条件满足了,就可以一起执行了
Semaphore,是信号量,可以控制并发的线程数

CountdownLatch
用来进行线程同步协作,它允许一个或多个线程一直等待,直到其他线程的操作执行完毕再执行。
其中构造参数用来初始化等待计数值,await()用来等待计数归零,countDown()用来让计数减一.
CountDownLatch 是共享锁的一种实现,它默认构造 AQS 的 state 值为 count。当线程使用 countDown方法时,其实使用了 tryReleaseShared 方法以CAS 的操作来减少 state ,直至 state 为 0 就代表所有的线程都调用了countDown方法。当调用 await 方法的时候,如果 state 不为0,就代表仍然有线程没有调用 countDown 方法,那么就把已经调用过 await 的线程都放入阻塞队列 Park ,并自旋 CAS 判断 state == 0,直至最后一个线程调用了 countDown ,使得 state == 0,于是阻塞的线程便判断成功,全部往下执行。

Semaphore
信号量,用来限制能同时访问共享资源的线程上限。acquire()获取许可,release()释放许可


http://lihuaxi.xjx100.cn/news/1710082.html

相关文章

浅谈RabbitMQ的延迟队列

Part 01、 延迟队列是什么 延迟队列代表了一种强大的消息传递机制,允许我们在将消息发送至RabbitMQ时,规定它们只能在未来某个预定的时间点被消费。这种特殊类型的消息被简称为"延迟消息"。 以RabbitMQ为例,它允许我们通过延迟…

【matplotlib】matplotlib的颜色表

【matplotlib】matplotlib的颜色表 文章目录 【matplotlib】matplotlib的颜色表1. 颜色表Reference 1. 颜色表 在使用matplotlib库进行绘图的时候,只需要指定关键字coloryour_color就能修改绘制的颜色了,具体的颜色表如下。 Reference https://finthon…

Python使用正则表达式去除base64编码前缀

Python使用正则表达式去除base64编码前缀 当我们做开发时,前端有可能会传给我我们base64编码字符串,如果我们想把它转成文件,需要去除前缀,代码如下: import rebase64_str "data:zip/png;base64,AAAAAAAAAAAAB…

SpringBoot_minio sdk使用自签名https证书错误处理

minio sdk使用自签名https证书错误处理 1.问题描述1.1 报错日志1.2 maven 依赖配置1.3 当前spring MinioClient配置 2.问题分析3.问题解决3.1 使用受信任的证书3.2 忽略证书验证3.2.1 minio客户端3.2.2 minio sdk 忽略证书验证3.2.2.1 拓展: 补充minioclient请求日志 4. 问题总…

A. Modulo Ruins the Legend 2022 ICPC-杭州

思路: (1)题目抽象为求(nsn*(n1)/2*d sum)%m的最小值 (2)由裴属定理,nsn*(n1)/2*d k1*g1(n,n*(n1)/2); (3)所以为求(k1g1 sum)%m ans的最小值; (4)即k…

ALPHA开发板网络方案说明

一. 简介 正点原子 ALPHA开发板,包括我们移植的 Uboot,都是参考了 NXP(恩智浦)官方的开发板的。 I.MX6UL/ULL 内部有个以太网 MAC 外设,也就是 ENET ,需要外接一个 PHY 芯片来实现网络通信功能&#…

QWidget快速美化-圆形蓝色单选框

将代码复制进QRadioButton的样式表 效果: 代码: QRadioButton{font:75 9pt "Arial";background:transparent;color:white;border:none; }QRadioButton:disabled{color:gray; }QRadioButton::indicator{width:12px;height:12px;border-radius:8px; }QRadioButton::i…

爱创科技携手洽洽食品,探索渠道数字化最优解!

坚果的下半场,是从吃到喝。 消费升级大潮下,健康养生理念逐渐深入人心。以“天然健康”为核心的食品新消费潮流正加速形成,一个个打着“美味与营养”黄金设定的品类风口正被不断创建,其中人气有增无减的当属植物基饮品。据相关报告…