【JavaEE】AQS原理

本文将介绍AQS的简单原理。


首先有个整体认识,全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架。常用的ReentrantLock、Semaphore、CountDownLatch等都有实现它。

本文参考:

深入理解AbstractQueuedSynchronizer只需15张图_abstractqueuedsynchronizer流程图-CSDN博客

 黑马程序员深入学习Java并发编程,JUC并发编程全套教程_哔哩哔哩_bilibili

从ReentrantLock的实现看AQS的原理及应用 - 美团技术团队 (meituan.com)

Java AQS 核心数据结构-CLH 锁 (qq.com)


AQS关键组成部分


state

state用来表示资源的状态(独占模式和共享模式),子类需要控制这个变量,就能够获取锁或者释放锁。

独占模式:只允许一个线程访问资源。

共享模式:允许多个线程访问资源。


CLH队列

这个队列保存的是没有获得锁资源进入阻塞的线程。使用的是头节点和尾结点来创建出这个双向队列。这个队列是先进先出的,不支持优先级。这里的CLH队列是AQS对于CLH锁的升级改后的应用。

该队列是对自旋锁的改进。那么自旋锁有上面缺点呢?

自旋锁


public class SpinLock {private AtomicReference<Thread> owner = new AtomicReference<Thread>();public void lock() {Thread currentThread = Thread.currentThread();// 如果锁未被占用,则设置当前线程为锁的拥有者// 这里就是自旋锁的核心部分while (!owner.compareAndSet(null, currentThread)) {}}public void unlock() {Thread currentThread = Thread.currentThread();// 只有锁的拥有者才能释放锁owner.compareAndSet(currentThread, null);}
}

第一个是锁饥饿问题。在锁竞争激烈的情况下,可能存在一个线程一直被其他线程”插队“而一直获取不到锁的情况。

第二是性能问题。在实际的多处理上运行的自旋锁在锁竞争激烈时性能较差。

CLH队列就把上述的两个问题解决了。

CLH队列过程(加解锁)

  1. 初始化一个 Tail 指向一个状态为false的空节点。
  2. 当t1线程获取锁,tail就指向t1线程,同时修改状态为true
  3. 当t2也想获得锁,此时t1还没释放,tail就指向t2,同时t1对应的节点。此时t2检查到t1节点为false,就开始轮询上一个节点的状态
  4. 当t1结束后,就把值修改为false
  5. 当t2轮询到值为false后,就是获取锁成功。

AQS升级CLH锁

  1. 扩展了每个节点的状态(waitStatus),从简单的true和false,扩展到如下部分。
  2. 维护前驱后继节点
    原始版本的 CLH 锁中,节点间甚至都没有互相链接。但是,通过在节点中显式地维护前驱节点,CLH 锁就可以处理“超时”和各种形式的“取消”:如果一个节点的前驱节点取消了,这个节点就可以滑动去使用前面一个节点的状态字段。
  3. 用阻塞等待代替自选操作
    当前一个节点释放锁之后,它会通知后一个节点获取锁。

条件变量

如果线程不满足条件变量后,那么它就要进入另外一种队列进行等待。每一个条件变量都会创建出一个队列。其中这个队列是单向的。


锁资源获取与释放

  • 独占式
    • acquire获取资源
    • release释放资源
  • 共享式
    • acquireShared获取资源
    • releaseShared释放资源

获得独占锁

acquire是个模板函数,模板流程就是线程获取共享资源,如果获取资源成功,线程直接返回,否则进入CLH队列,直到获取资源成功为止,且整个过程忽略中断的影响。

    public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}

释放独占锁

AQS中提供了release模板函数来释放资源,模板流程就是线程释放资源成功,唤醒CLH队列的第二个线程节点。

    public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;}

获得共享锁

acquireShared是个模板函数,模板流程就是线程获取共享资源,如果获取到资源,线程直接返回,否则进入CLH队列,直到获取到资源为止,且整个过程忽略中断的影响。

    public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0)doAcquireShared(arg);}

释放共享锁

AQS中提供了releaseShared模板函数来释放资源,模板流程就是线程释放资源成功,唤醒CHL队列的第二个线程节点(首节点的下个节点)

    public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;}

自定义锁

下面我们自定义一个锁,该锁内部的同步器是不可重入的独占锁

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;// 自定义锁
public class MyLock implements Lock {// 独占锁 同步器类public class MySync extends AbstractQueuedSynchronizer {@Overrideprotected boolean tryAcquire(int acquires) {if (compareAndSetState(0, 1)) {// 加锁,并设置成当前线程setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}@Overrideprotected boolean tryRelease(int acquires) {setExclusiveOwnerThread(null);// state是被volatile修饰的,exclusiveOwnerThread 没有被volatile修饰// 所以设置exclusiveOwnerThread成null要放在前面setState(0);return true;}protected Condition newCondition() {return new ConditionObject();}// 是否持有独占锁@Overrideprotected boolean isHeldExclusively() {return getState() == 1;}}private MySync sync = new MySync();// 加锁,不成功进入等待队列@Overridepublic void lock() {sync.acquire(1);}// 加锁,不成功进入等待队列,可被中断@Overridepublic void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}// 尝试加锁一次,不成功直接返回@Overridepublic boolean tryLock() {return sync.tryAcquire(1);}// 尝试加锁(带超时的),不成功进入等待队列,可被中断@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(time));}// 解锁@Overridepublic void unlock() {// 这里release本质上调用的是tryRelease方法 解锁并唤醒等待队列中的线程// 如果调用的是release方法,就不会唤醒等待的线程sync.release(1);}// 创建一个Condition@Overridepublic Condition newCondition() {return sync.newCondition();}
}class Test {public static void main(String[] args) {MyLock lock = new MyLock();new Thread(() -> {lock.lock();long now = System.currentTimeMillis();try {System.out.println("t1 获取锁");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println("t1 释放锁");System.out.println("t1 耗时:" + (System.currentTimeMillis() - now));lock.unlock();}}, "t1").start();new Thread(() -> {lock.lock();try {System.out.println("t2 获取锁");} finally {System.out.println("t2 释放锁");lock.unlock();}}, "t2").start();}
}

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

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

相关文章

[C++]TinyWebServer

TinyWebServer 文章目录 TinyWebServer1 主体框架2 Buffer2.1 向Buffer写入数据2.2 从Buffer读取数据2.3 动态扩容2.4 从socket中读取数据2.5 具体实现 3 日志系统3.1 生产者-消费者模型3.2 数据一致3.3 代码 4 定时器4.1 调整堆中元素操作4.2 堆的操作4.2.1 增4.2.2 删4.2.3 改…

【AI资讯早报】AI科技前沿资讯概览:2024年7月23日早报

【AI资讯早报&#xff0c;感知未来】AI科技前沿资讯概览&#xff0c;涵盖了行业大会、技术创新、应用场景、行业动态等多个方面&#xff0c;全面展现了AI领域的最新发展动态和未来趋势。 1.阿里巴巴发布ESG报告&#xff0c;AI赋能医疗与助老助残 2024年7月22日&#xff0c;阿里…

ElasticSearch-分词器介绍

Analysis Analysis文本分析&#xff0c;也叫分词&#xff0c;是把全文本转换为一系列单词的过程。 Analyzer的组成 通常Analyzer由三个部分组成。 Character Filters&#xff1a;针对原始文本处理&#xff0c;例如去除html标签等。Tokenizer&#xff1a;按照一定的规则&…

微信小程序-应用,页面和组件生命周期总结

情景1&#xff1a;小程序冷启动时候的顺序 情景2: 使用navigator&#xff08;保留并打开另一个页面&#xff09;和redirect&#xff08;关闭并打开另一个页面&#xff09;的执行顺序 情景3&#xff1a;切后台和切前台

Linux——组管理和权限管理

目录 组管理 Linux 组基本介绍 文件/目录所有者 组的创建 查看&修改文件/目录所在组 改变用户所在组 权限管理 基本介绍 rwx 文件/目录权限详解 chmod 修改文件或目录权限 chown 修改文件所有者 组管理 Linux 组基本介绍 关于第二张图中问题&#xff0c;答案…

windows11 ,ubuntu20.04双系统,ubuntu没有wifi的解决方式

Ubuntu20系统安装完后没有WIFI_modprobe:error:could not insert 8852be:invalid a-CSDN博客 ubuntu 20.04 设置国内镜像源&#xff08;阿里源、清华源&#xff09;_ubuntu 20.04 镜像源-CSDN博客 Ubuntu20.04无法直接连接无线网络 解决方法_ubuntu20.04连接wifi-CSDN博客 …

Linux下如何使用GitLab进行团队协作

在Linux系统中&#xff0c;使用GitLab进行团队协作是非常常见的做法。GitLab是一个基于Git的代码托管和项目管理平台&#xff0c;可以帮助团队成员共享、追踪和管理代码的开发过程。本文将介绍如何在Linux系统中安装和配置GitLab&#xff0c;以及一些常用的团队协作功能。 安装…

【Qt】Qt的坐标转换(mapToGlobal)

1、QPoint QWidget::mapToGlobal(const QPoint &pos) const 将小部件坐标转换为全局坐标。mapToGlobal(QPoint(0,0))可以得到小部件左上角像素的全局坐标。2、QPoint QWidget::mapToParent(const QPoint &pos) const 将小部件坐标转换为父部件坐标。如果小部件没有父部…

Jmeter之count函数

counter函数 1、功能解释 count函数--计数器&#xff0c;每调用这个函数一次&#xff0c;它就会自动加1。它有两个参数&#xff0c;第一个参数是布尔型的&#xff0c;只能设置成 “TRUE”或者“FALSE”&#xff0c;如果是TRUE&#xff0c;那么每个用户有自己的计数器&#xf…

常用的网络爬虫工具推荐

在推荐常用的网络爬虫工具时&#xff0c;我们可以根据工具的易用性、功能强大性、用户口碑以及是否支持多种操作系统等多个维度进行考量。以下是一些常用的网络爬虫工具推荐&#xff1a; 1. 八爪鱼 简介&#xff1a;八爪鱼是一款免费且功能强大的网站爬虫&#xff0c;能够满足…

二叉树---验证二叉搜索树

题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 思路&…

算法整理——【动态规划练习(8)子序列】

本博客我们梳理用动态规划方法解决子序列问题。 一、最长公共子序列 题目为1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09;&#xff0c;给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度。如果不存在公共子序列&#xff0c;…

vxe-table——实现切换页码时排序状态的回显问题(ant-design+elementUi中table排序不同时回显的bug)——js技能提升

之前写的后台管理系统&#xff0c;都是用的antdelement&#xff0c;table组件中的【排序】问题是有一定的缺陷的。 想要实现的效果&#xff1a; antv——table组件一次只支持一个参数的排序 如下图&#xff1a; 就算是可以自行将排序字段拼接到列表接口的入参中&#xff0c…

解决Error: Not Found:Page[5][-1,81] at view.umd.min.js

场景说明&#xff1a; uniapp使用组件&#xff0c;在APP环境出现&#xff0c;在H5环境正常。单页面上多处使用该组件&#xff0c;使用同名参数设置数据&#xff0c;应用切换效果时&#xff0c;触发请求不同接口&#xff0c;返回数据格式不同。使用v-if时出现&#xff0c;使用v…

环信+亚马逊云科技服务:助力出海AI社交应用扬帆起航

随着大模型技术的飞速发展&#xff0c;AI智能体的社交体验得到了显著提升&#xff0c;AI社交类应用在全球范围内持续火热。尤其是年轻一代对新技术和新体验的热情&#xff0c;使得AI社交产品在海外市场迅速崛起。作为领先的即时通讯解决方案提供商&#xff0c;环信与亚马逊云科…

计算机体系结构|| 再定序缓冲(ROB)原理(6)

实验6 再定序缓冲&#xff08;ROB&#xff09;原理 6.1实验目的 &#xff08;1&#xff09;加深对指令级并行性及其开发的理解。 &#xff08;2&#xff09;加深对基于硬件的前瞻执行的理解。 &#xff08;3&#xff09;掌握 ROB 在流出、执行、写结果确认4 个阶段所进行的…

vue3 -layui项目-左侧导航菜单栏

1.创建目录结构 进入cmd,先cd到项目目录&#xff08;项目vue3-project&#xff09; cd vue3-project mkdir -p src\\views\\home\\components\\menubar 2.创建组件文件 3.编辑menu-item-content.vue <template><template v-if"item.icon"><lay-ic…

SQL injection UNION attacks SQL注入联合查询攻击

通过使用UNION关键字&#xff0c;拼接新的SQL语句从而获得额外的内容&#xff0c;例如 select a,b FROM table1 UNION select c,d FROM table2&#xff0c;可以一次性查询 2行数据&#xff0c;一行是a&#xff0c;b&#xff0c;一行是c&#xff0c;d。 UNION查询必须满足2个条…

医学深度学习与机器学习融合的随想

医学深度学习与机器学习融合的随想 近年来&#xff0c;深度学习&#xff08;图像类&#xff09;和机器学习在医学领域的应用取得了飞速发展&#xff0c;为医学影像分析、疾病诊断和预后预测等领域带来了革命性的变革。深度学习擅长从复杂数据中提取高层次特征&#xff0c;而机…

java面试题,有synchronized锁,threadlocal、数据可以设置默认值、把redis中的json转为对象

有面试题&#xff0c;有synchronized锁&#xff0c;threadlocal 一、面试题小记二、加锁synchronized1. 先看代码2. synchronized 讲解2.1. 同步代码块2.2. 同步方法2.3. 锁的选择和影响2.4. 注意事项2.5 锁的操作&#xff0c;手动释放锁&#xff0c;显式地获取锁&#xff08;属…