【Java多线程JUC入门详解】AQS原理详解

news2024/12/23 4:52:56

AQS

概述

AbstractQuenedSynchronizer抽象的队列式同步器, 在java.util.concurrent.locks包下。

我们常用的ReentrantLock中有一个抽象静态内部类Sync,就继承自AbstractQuenedSynchronizer

abstract static class Sync extends AbstractQueuedSynchronizer {
}

而new 一个ReentrantLock对象的话,构造函数时这样的

public ReentrantLock() {
    // 默认创建的是非公平
    sync = new NonfairSync();
}

而这个NonfairSync则继承自SyncSync,就继承自AbstractQuenedSynchronizer

AQS是JUC的基石,有很多个类( (ReentrantLock 、CountDownLatch 、ReentrantReadWriteLock、Semaphore) )的实现基于这个类,因为这些类都需要包含的一些功能由AQS提供。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ZZs7bsU-1672884624584)(JUC多线程.assets/20210702093110465.png)]

其通过内置的CLH(FIFO)队列(CLH是三个科学家的名字,为一个单向链表实现的队列,AQS中为其变种,即实际为双向链表)来完成资源获取线程的排队工作,将每条将要去抢占资源的线程封装成一个Node节点来实现锁的分配,有一个int变量 state 表示持有锁的状态,通过CAS对state 进行修改(0表示没有,1表示阻塞)

加锁会导致阻塞、有阻塞就需要排队,实现排队必然需要队列 。 如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的结点(Node) ,通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的效果

锁,面向锁的使用者(定义了程序员和锁交互的使用层API,隐藏了实现细节,你调用即可)

AQS同步器,面向锁的实现者。 由Java并发大神Douglee,提出统一规范并简化了锁的实现,屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制等

AQS有一个内部类Node,Node有pre和next的属性。Node有head、tail属性。从源码可以看出。其内部有一个双向链表,而这个双向链表就是CLH队列变种的队列。

AQS有一个int属性state ,用来表示阻塞状态(锁的状态)。0表示可以获取,1则表示需要阻塞

Node的部分字段含义:

原理

内部就是一个由双向链表实现的队列,队列中的节点node就代表了一个线程。

队列中有一个状态用来表示阻塞状态,0表示可以获取锁,1表示要阻塞线程。

在获取锁的时候,会去CAS修改state,如修改成功则获取到锁,

如果出现竞争,公平锁会把线程节点添加到队尾,并park阻塞,等待其他线程执行完毕执行unpark唤醒

如果出现竞争,则说明当前线程 与 队列头的线程有争抢,谁抢到算谁的。

可重入锁则会将state修改到大于1

从lock()方法阅读AQS源码

final void lock() {
    if (compareAndSetState(0, 1))
        // 第一个进来的线程
        // 这里调用父类AQS的方法,CAS修改state ,修改成功就把当前线程设置上,方便之后重入
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 失败的话在这个方法里继续尝试获得
        acquire(1);
}
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire需要子类去实现,有公平锁、非公平锁等的实现,构造函数指定的话,创建的是非公平锁

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            // 可能上一个线程刚好执行完了,这里获取锁成功
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        // 这里说明目前持有锁的是当前线程,这里可重入,state需要增加到大于1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

tryAcquire失败了的话,则先addWaiter然后把新的node传进去执行acquireQueued

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        // 如果不是第一次进来,则直接尝试把node加入队尾
        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) { // Must initialize
            // 第一次进来会先创建一个哨兵节点作为头节点,同时尾节点也指向了它
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 然后自旋,到这里
            // 将当前线程的节点加入队尾,如下图所示
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AebzrbwH-1672884624586)(JUC多线程.assets/image-20220829101905167.png)]

acquireQueued 如果获取不到锁的话,会执行 shouldParkAfterFailedAcquire和 parkAndCheckInterrupt

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // ws初始为0
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        // 线程准备好了,直接返回true
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 设置为-1,-1代表线程准备好了等待资源释放
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
private final boolean parkAndCheckInterrupt() {
    // 这里将线程阻塞,直到unpark。
    // unpark是在unlock中执行的
    LockSupport.park(this);
    return Thread.interrupted();
}
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)) {
                // 如果当前节点的前一个节点是头节点(傀儡节点)
                // 则说明当前节点是第一个节点,且获取锁成功,就会进入这里
                // 获取锁成功,则代表了上一个线程释放了锁也执行了unpark,所以下面那个方法被唤醒,继续自旋,执行到这里
                // 会重新设置头节点,进入setHead中,也就是将当前节点设置为傀儡节点
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

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

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

相关文章

1.4日报

验收了TestMrlController 历经千辛万苦给电脑安装了vpn 因为昨天系统损坏&#xff0c;今天重新配置postman和navicat 遇到的困难及解决 linux安装vpn时官方给的教程&#xff1a; 可是ubnutu里没有yum&#xff0c;我得先下载yum 而默认镜像源里找不到yum软件包&#xff0c;…

单调栈 单调队列 专题

文章目录一、单调栈1、问题模型2、实现过程&#xff1a;3、代码实现4、规律总结5、题目练习二、单调队列1、问题模型2、实现过程&#xff1a;3、代码实现4、规律总结5、题目练习三、总结一、单调栈 1、问题模型 主要解决一类问题&#xff1a; O(n)O(n)O(n) 求数列中每个元素左…

PaddleNLP系列课程一:Taskflow、小样本学习、FasterTransformer

文章目录一、Taskflow1.1 前言1.2 Taskflow应用介绍1.2.1 词法分析1.2.2 命名实体识别1.2.3 文本纠错1.2.4 句法分析1.2.5 情感分析1.2.6 文本生成应用&#xff08;三行代码体验 Stable Diffusion&#xff09;1.2.7 使用技巧&#xff08;保存地址、批量推理&#xff09;二、 小…

Excel怎么转换成PDF?教你两招轻松搞定

Excel怎么转换成PDF&#xff1f;相信在工作中大家都或多或少需要转换文件的格式&#xff0c;我们会根据工作需求将word、excel、PPT、图片等文件转换成PDF文件。Excel表格是我们经常使用的一款录入数据的文件类型&#xff0c;因为excel文件打开查看时不是很方便&#xff0c;我们…

时序数据库 TDengine 3.0 参数体系使用方式汇总

在日常使用 TDengine 时&#xff0c;参数是用户们无法绕开的重要一环。深入了解参数的属性&#xff0c;生效范围&#xff0c;查询更改方式等会让我们在使用数据库的过程中更加节时高效&#xff0c;也有助于我们更加深入地理解数据库的架构体系。在 3.0 版本中&#xff0c;TDeng…

参数校验(Validator)

为什么要用validator 实战演练 1. Validated 声明要检查的参数 2. 对参数的字段进行注解标注 3. 在全局校验中增加校验异常 4. 测试 自定义参数注解 1. 比如我们来个 自定义身份证校验 注解 2. 然后自定义Validator 3. 使用自定义的注解 4.使用groups的校验 5.restful…

209数组-长度最小的子数组

题目 链接&#xff1a;209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 思路 可以采用暴力&#xff0c;两个for循环&#xff0c;不断寻找符合条件的子序列。时间复杂度为O(n^2) 代码&#xff1a; class Solution { public:int minSubArrayLen(int target, v…

【BP靶场portswigger-服务端6】信息泄露漏洞-5个实验(全)

目录 一、信息泄露漏洞 1、简述&#xff1a; 2、危害&#xff1a; 3、示例&#xff1a; 4、漏洞的产生 5、影响 6、严重程度判定 二、发现和利用信息泄露漏洞 1、模糊测试 2、使用BP扫描仪 3、使用Burp的相关工具 4、工程信息响应 三、常见来源 1、示例&#xff…

【数据篇】34 # 如何处理多元变量?

说明 【跟月影学可视化】学习笔记。 从数据到图表展现 2014 年北京市的天气历史数据&#xff08;csv 格式&#xff09; 这里使用QCharts 图表库折线图来展示平均气温 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" …

PDF转换成word免费版网页版哪个好?办公就用它

我们在工作中经常需要对PDF文件进行编辑&#xff0c;所以可以将PDF文件转换为word文件&#xff0c;这样就可以随意编辑我们想要编辑的内容了&#xff0c;不过有些小伙伴不喜欢下载客户端&#xff0c;但是又不知道PDF转换成word免费版网页版哪个好&#xff0c;为了帮助大家更好的…

微信小程序是如何设计百亿级用户画像分析系统的?

导语 | “We分析”是微信小程序官方推出的数据分析平台&#xff0c;其中画像洞察是其中一个非常重要的功能模块。微信开发工程师钟文波将描述We分析画像系统各模块是如何设计&#xff0c;在介绍基础标签模块之后&#xff0c;重点讲解用户分群模块设计。希望相关的技术实现思路&…

使用OpManager Plus进行网络运行管理

什么是网络运行管理 网络运行管理会不断跟踪网络设备及其操作&#xff0c;并在出现任何网络故障时通知技术人员团队&#xff0c;以便消除它们并主动加强网络免受停机的影响。 为什么网络运行管理很重要 借助网络操作管理工具&#xff0c;管理员可以深入了解网络的最微小细节…

springboot多模块工程单元测试jacoco统计代码覆盖率总结

jacoco统计代码覆盖率的文章一搜一大堆&#xff0c;方法也很简单&#xff0c;就是在pom中引用两个插件&#xff1a; maven-surefire-plugin jacoco-maven-plugin 其中jacoco-maven-plugin的关键配置为要有两个execution&#xff1a; 一个goal是prepare-agent&#xff0c;即…

【JavaEE】初识Spring

目录 一、创建Spring项目 二、介绍理论 一、创建Spring项目 通过maven创建Spring项目&#xff0c;我们需要借助一些工具完成工程的创建。 1.pom.xml 2.一部分示例代码 Spring提供了一个Spring Initializr使用网页版本。 然后将得到文件的内容用idea打开。然后执行会显示未连…

集群环境下解决定时任务重复执行的问题【案例分享】

【辰兮要努力】&#xff1a;hello你好我是辰兮&#xff0c;很高兴你能来阅读&#xff0c;昵称是希望自己能不断精进&#xff0c;向着优秀程序员前行&#xff01; 博客来源于项目以及编程中遇到的问题总结&#xff0c;偶尔会有读书分享&#xff0c;我会陆续更新Java前端、后台、…

机器学习中的数学原理——似然函数

这个专栏主要是用来分享一下我在 机器学习中的 学习笔记及一些 感悟&#xff0c;也希望对你的学习有帮助哦&#xff01;感兴趣的小伙伴欢迎 私信或者 评论区留言&#xff01;这一篇就更新一下《 白话机器学习中的数学——似然函数》&#xff01;什么是似然函数似然函数 定义. 在…

电商让客户等待的话术

在电商平台购物时&#xff0c;在购物前后客户难免会产生一些问题&#xff0c;客服需要先进行核对&#xff0c;或者需要转接给专员进行接待&#xff0c;在这期间&#xff0c;客户需要等候&#xff0c;这时候需要给客户发送一些等候话术。前言在电商平台购物时&#xff0c;在购物…

uni-app动态修改manifest.json中的参数

以根据不同环境配置不同的h5运行路径为例&#xff0c;动态修改h5.router.base 假设当前有三种环境 process.env.NODE_ENV developmentprocess.env.NODE_ENV testprocess.env.NODE_ENV production 在src根目录下创建modifyManifest.js文件&#xff08;一定是src&#xff09;…

渗透测试工程师的职业发展

前段时间看了一个大哥写的程序员的职业发展&#xff0c;感触很深&#xff0c;这几天晚上就参考大哥的思路结合自身的经历写一下渗透工程师的职业发展之路&#xff0c;顺便也让迷茫中的小伙伴们有个参考。 很多干渗透、安全服务、安全运维的人在干了3-5年后面对迷茫期&#xff…

2022年度猫狗粮销售数据:十大热门品牌排行榜,哪些品牌入围?

当前&#xff0c;“吸猫撸狗”正成为当代年轻人新的生活方式&#xff0c;越来越多的人乐意为了自己的“毛孩子”买单&#xff0c;这使得宠物经济快速发展。伴随着宠物食品概念越来越被消费者所接受&#xff0c;目前&#xff0c;我国宠物食品行业呈现爆发式增长&#xff0c;宠物…