AQS和同步器工具类

一、 同步框架AbstractQueuedSynchronizer

Java并发编程核心在于java.concurrent.util包
而juc当中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这个行为的抽象就是基于AbstractQueuedSynchronizer简称AQS,AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器,主要结构是双向链表的FIFO队列(尾插法),如果线程抢不到,就进这个链表排队,之后再等待被唤醒;

AQS具备特性
1、阻塞等待队列
2、共享/独占
3、公平/非公平
4、可重入
5、允许中断

二、并发编程包依赖于AQS的内部实现

Java.concurrent.util当中同步器的实现如Lock,Latch,Barrier等,都是基于AQS框架实现
一般通过定义内部类Sync继承AQS,将同步器所有调用都映射到Sync对应的方法

三、AQS框架-管理状态

1、AQS内部维护属性volatile int state (32位)
state表示资源的可用状态

2、State三种访问方式
getState()、setState()、compareAndSetState()

3、AQS定义两种资源共享方式
Exclusive-独占,只有一个线程能执行,如ReentrantLock
Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch

4、AQS定义两种队列
同步等待队列
条件等待队列

四、ReentrantLock

这里 state 代表 :同一线程占用的次数
应用场景:
虽然允许多个线程进行访问,但是通过state机制保证同一时刻只有一个线程能获取资源。
可以替代synchronized关键字,提供更灵活的加锁和解锁操作,支持公平性和非公平性,可重入。

底层原理:
支持可重入性:reentrantLock.lock(); 同一个线程可用多次,每一次state就加1;同理reentrantLock.unlock(); 每次给state 减1,减到0就允许其他线程来抢。
state>0 代表有锁状态,state=0代表无锁状态;

任务开始前调用 reentrantLock.lock(); state就加1,表示同个线程占用的次数
任务结束后调用 reentrantLock.unlock(); state就减1


ReentrantLock reentrantLock = new ReentrantLock(false);public static void print() {reentrantLock.lock(); //给state 加1System.out.println(Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}finally {reentrantLock.unlock(); //给state 减1}}

源码中这一段取决了锁是不是 可重入
源码中,这里会判断是不是当前线程在获取资源,是的话就加起来,再set;
而其他不可重入的锁没有这个逻辑,甚至不用去记录是不是当前线程获取的,所以不可重入

//源码中,这里会判断是不是当前线程在获取资源,是的话就加起来,再set;
//而其他不可重入的锁没有这个逻辑,甚至不用去记录是不是当前线程获取的,所以不可重入
else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) throw new Error("Maximum lock count exceeded");setState(nextc);return true;
}

公平与非公平的区别

1)公平锁的实现:调用acquire(1)函数,其实公平锁也没有那么老实,这里一上来还是会去尝试抢一下锁的 tryAcquire(arg),抢不到了才入队列排队acquireQueued(),再中断自己的运行selfInterrupt();


static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}
//其实公平锁也没有那么老实,这里一上来还是会去尝试抢一下锁的 tryAcquire(arg),抢不到了才入队列排队acquireQueued(),中断自己的运行selfInterrupt();
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}

2)非公平锁的实现
先尝试抢占锁,就是把state从0改成1, compareAndSetState(0, 1),然后将当前的执行线程改成自己 setExclusiveOwnerThread(Thread.currentThread())
失败的话就调用公平锁的流程acquire(1); 但还是一样,要先尝试抢一下锁,抢不到才乖乖去排队,所以它其实前后尝试了两次抢锁,这个刺头

为什么要插队呢?因为插队可以减少cpu去等待队列中唤醒线程的时间代价
只有在别人刚好完成任务退出后,cup要去队列中唤醒线程的那一瞬间有机会被插队成功

static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;final void lock() {//先尝试抢占锁把state从0改成1,然后将当前的执行线程改成自己if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}

唤醒规则:
不管是不是公平锁,唤醒方式是用的同一套方法;
一般是唤醒队列中排队的头节点线程,但如果该线程任务状态异常,就是waitStatus >0,那它就会从尾部开始找一个正常状态的线程任务去唤醒;
这里不清楚它为什么不直接清理掉头节点,然后再顺序找下去,居然去尾部找

private void unparkSuccessor(Node node) {/** If status is negative (i.e., possibly needing signal) try* to clear in anticipation of signalling.  It is OK if this* fails or if status is changed by waiting thread.*/int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);/** Thread to unpark is held in successor, which is normally* just the next node.  But if cancelled or apparently null,* traverse backwards from tail to find the actual* non-cancelled successor.*/Node s = node.next;if (s == null || s.waitStatus > 0) {s = null;for (Node t = tail; t != null && t != node; t = t.prev) //从尾巴倒着找到第一个正常的if (t.waitStatus <= 0)s = t;}if (s != null)LockSupport.unpark(s.thread);}

五、 CountDownLatch

这里 state 代表 :还有多少线程能拿到资源,资源被拿走后不还回去,一次性

static CountDownLatch countDownLatch = new CountDownLatch(5);
新建时,初始化state = 某值 ;
每个线程完成任务后,调用 CountDownLatch.countDown() 将state减1
调用 CountDownLatch.await() 的主线程会被阻塞,直到state 减到0,也就是所有任务完成了,主线程才往下走;

六、Semaphore

这里 state 代表 :最多有多少线程同时能拿到资源,且资源被拿走后可以还回去给别的线程拿,动态变化

static Semaphore semaphore = new Semaphore(6);
新建时,初始化state = 某值 ;
任务开始前先调用semaphore.acquire() 给state 减1,表示占用了一个资源
任务完成后调用semaphore.release(); 给state 加1,表示把资源还回去
减到state = 0 时,则表示资源不够了,要抢的线程进队列排队

七、CyclicBarrier

这里 state 代表 :还有多少线程能拿到资源,但资源被拿走后统一在最后结束还回去,一波流
CyclicBarrier cyclicBarrier = new CyclicBarrier(7)
新建时,初始化state = 某值;
每次任务执行完调用cyclicBarrier.await(); state减1
减到state = 0 时,表示所有线程都干完活了,所有等待的线程继续执行,包括主线程。然后重新赋值 state=初始值;
允许一组线程互相等待,直到到达某个公共屏障点。

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

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

相关文章

关于多线程的理解

#系列文章 关于时间复杂度o(1), o(n), o(logn), o(nlogn)的理解 关于HashMap的哈希碰撞、拉链法和key的哈希函数设计 关于JVM内存模型和堆内存模型的理解 关于代理模式的理解 关于Mysql基本概念的理解 关于软件设计模式的理解 关于Redis知识的理解 文章目录 前言一、线程…

Docker基本使用和认识

目录 基本使用 镜像仓库 镜像操作 Docker 如何实现镜像 1) namespace 2) cgroup 3) LXC Docker常见的网络类型 bridge网络如何实现 基本使用 镜像仓库 镜像仓库登录 1)docker login 后面不指定IP地址&#xff0c;则默认登录到 docker hub 上 退出 2)docker logo…

P5711 【深基3.例3】闰年判断

1. 题目链接 https://www.luogu.com.cn/problem/P5711 P5711 【深基3.例3】闰年判断 2. 题目描述 题目描述&#xff1a;判断一个数是否是闰年 输入&#xff1a;输入一个整数n 输出&#xff1a;输出1或0&#xff0c;如果是闰年&#xff0c;输出1&#xff0c;否则输出0 3. 我的…

大数据经典sql

一、连续登录问题 问题&#xff1a;1&#xff09;、每个用户连续登录最大天数 2&#xff09;、连续登录大于三天的用户数 分析&#xff1a;本质都是计算用户连续登录天数 方案一&#xff1a;利用排序窗口 select a.user_id,a.date_rslt,count(1) as cnt from (select t.…

进入容器修改内容_提交改变后的镜像_镜像保存成tar压缩包离线传输_镜像传输_镜像推送到公共仓库---分布式云原生部署架构搭建009

然后再来看,进入docker内部去看看. 用 docker exec -it imgid /bin/bash 这样就可以进入容器内部 而且关于,镜像的,内部放到什么地方了,都可以找到比如 在hub.docker的地址里面,找到nginx可以看到,对应的 /usr/share/nginx/html 可以看到这个路径. 然后去看看,进入到/usr…

Android 数据库

帮助类对象中的getWritableDatabase 和 getReadableDatabase都可以帮助我们获取一个数据库操作对象SqliteDatabase. 区别&#xff1a; getReadableDatabase: 先尝试以读写方式打开数据库&#xff0c;如果磁盘空间满了&#xff0c;他会重新尝试以只读方式打开数据库。 getWr…

如何恢复未格式化分区数据?看这里!

什么是未格式化分区&#xff1f; 未格式化或RAW文件系统的分区无法被Windows操作系统识别和挂载&#xff0c;因此&#xff0c;Windows会提示你进行格式化以创建新的文件系统。注意&#xff0c;不要进行格式化。通常&#xff0c;文件系统变为未格式化或RAW会出现以下常见错误消…

专业VS学校:高考后的选择困境与解决之道

随着2024年高考的落幕&#xff0c;数百万考生站在了人生新的十字路口。分数的揭晓&#xff0c;既是对过往努力的肯定&#xff0c;也带来了新的选择难题。在专业与学校的权衡中&#xff0c;考生们往往陷入两难&#xff1a;是追随内心选择心仪的专业&#xff0c;还是选择一个知名…

大学选专业还是选学校好 哪个更重要

​​​​​​​如果你的分数不理想&#xff0c;建议先选专业。如果你是高分考生&#xff0c;建议先选学校。具体来看一下&#xff01; 选专业还是选学校好 如果你是高分考生&#xff0c;建议先选学校 1 可以借助学校的名气 我们都知道&#xff0c;学校越有名&#xff0c;能…

(深度学习记录)第TR5周:Transformer中的位置编码详解

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 &#x1f3e1;我的环境&#xff1a; 语言环境&#xff1a;Python3.11.4编译器&#xff1a;Jupyter Notebooktorcch版本&#xff1a;2.0.…

【Matlab编程学习】 | matlab语言编程基础:常用图形绘制基础学习

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

传输层udp和tcp协议格式

UDP协议 UDP协议端格式 udp的前八个字节是报头&#xff0c;后面部分就是有效载荷。而目的端口号就保证了udp向应用层交付的问题。 而针对于报头和有效载荷分离是根据固定八字结的报头长度。数据的长度就是取决于报头中udp长度字段的大小来确定udp报文长度&#xff0c;因此也可…

Apifox 中如何处理加密或编码过的响应数据?

接口返回的响应数据有时是经过编码或加密处理的&#xff0c;要转换成可读的明文&#xff0c;可以使用 Apifox 内置的 JS 类库、或者通过调用外部编程语言 &#xff08;如 Python、JavaScript 等&#xff09; 来进行处理。 例如&#xff0c;一个经过 Base64 编码的数据可以通过…

可视化数据科学平台在信贷领域应用系列六:自动机器学习(上篇)

在现代数据驱动的世界中&#xff0c;机器学习已经成为解决复杂问题和推动创新的重要手段。然而&#xff0c;传统的机器学习模型开发过程复杂且耗时&#xff0c;包括数据预处理、特征工程、模型选择、参数调优和模型评估等多个步骤环节&#xff0c;需要模型开发人员具备丰富的专…

AI“音乐创作”横行给音乐家带来哪些隐忧

​​​​​​​近日&#xff0c;200多名国际乐坛知名音乐人联署公开信&#xff0c;呼吁AI开发者、科技公司、平台和数字音乐服务商停止使用人工智能(AI)来侵犯并贬低人类艺术家的权利&#xff0c;具体诉求包括&#xff0c;停止使用AI侵犯及贬低人类艺术家的权利&#xff0c;要求…

Nginx配置文件解析与实例详解:打造高效稳定的网站服务

Nginx 是一种高性能的 HTTP 和反向代理服务器,同时也提供了 IMAP/POP3 代理服务器功能。由于其高并发处理能力、低内存消耗和灵活的配置能力,Nginx 被广泛应用于 Web 服务器、反向代理、负载均衡和 API 网关等场景。本文将详细解析 Nginx 的配置文件结构,并通过实例演示如何…

wstring与wchar_t

文章目录 1、介绍2、相关函数 1、介绍 wstring 是由宽字符wchar_t构成字节列表。一个wchar_t字符的大小是2 字节&#xff0c;char字符大小是1字节。 2、相关函数 QString qs"hello"; QString fileName "000000000003.xls"; std::wstring ws fileName.…

vite中使用scss技巧

一、样式混合 1.普通用法 mixin flex() {display: flex;justify-content: space-around;align-items: center; }//使用方法 .legend_box_item {width: 50%;height: 10px;include flex; }2.传递参数&#xff0c;参数后面的值为默认值 mixin flex($justify: flex-start, $alig…

html + css + js 实现简易轮播图

html css js 实现简易轮播图 code <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…

Java面试题:mysql执行速度慢的原因和优化

Sql语句执行速度慢 原因 聚合查询 多表查询 表数据量过大查询 深度分页查询 分析 sql的执行计划 可以使用EXPLAIN或者DESC获取Mysql如何执行SELECT语句的信息 直接在select语句前加关键字explain/desc 得到一个执行信息表 信息字段分析 possible_keys:可能使用到的索…