【一知半解】AQS

🚀 优质资源分享 🚀

学习路线指引(点击解锁)知识定位人群定位
🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

什么是AbstractQueuedSynchronizer(AQS)

字面意思是抽象队列同步器,使用一个voliate修饰的int类型同步状态,通过一个FIFO队列完成资源获取的排队工作,把每个参与资源竞争的线程封装成一个Node节点来实现锁的分配。

AbstractQueuedSynchronizer源码

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizerimplements java.io.Serializable {private transient volatile Node head;//链表头private transient volatile Node tail;//链表尾private transient Thread exclusiveOwnerThread;//持有锁的线程private volatile int state;//同步状态,0表示当前没有线程获取到锁static final class Node {//链表的Node节点类volatile int waitStatus;//当前节点在队列中的状态volatile Node prev;//前置节点volatile Node next;//后置节点volatile Thread thread;//当前线程}
}

AQS同步队列的基本结构

Node.waitStatus的说明

状态值描述
0节点默认的初始值
SIGNAL=-1线程已经准备好,等待释放资源
CANCELLED=1获取锁的请求线程被取消
CONDITION=-2节点在队列中,等待唤醒

state为什么要用volatile修饰?

  1. 可见性,一个线程对变量的修改可以立即被别的线程感知到
  2. 有序性,禁止指令重排

AQS获取锁步骤

  public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
  1. 当一个线程获取锁时,首先判断state状态值是否为0
  2. 如果state==0,则通过CAS的方式修改为非0状态
  3. 修改成功,则表明获取锁成功,执行业务代码
  4. 修改失败,则把当前线程封装为一个Node节点,加入到队列中并挂起当前线程
  5. 如果state!=0,则把当前线程封装为一个Node节点,加入到队列中并挂起当前线程

AQS获取锁过程

首先调用tryAcquire去修state的状态值,成功就获取当前锁;失败则加入当前等待队列中,然后挂起线程。

tryAcquire

AQS的源码中tryAcquire是一空实现,需要它的子类去实现这个空方法。因为在AQS中虽然公平锁非公平锁的都是基于一个CLH去实现,但是在获取锁的过程中略有不同。

protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}

公平锁FairSync#tryAcquire

protected final boolean tryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();int c = getState();//获取同步器的状态if (c == 0) {//当前没有线程获取到锁//首先判断祖宗节点的线程是否当前线程一样if (!hasQueuedPredecessors() &&//更改state的状态值为非0compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}//如果锁持有者的线程是当前线程,则可放行,锁的重入else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}}
/**
* 判断祖宗节点的线程是否当前线程一样
* 傀儡节点的下个节点
*/
public final boolean hasQueuedPredecessors() {Node t = tail;Node h = head;Node s;//头节点的下个节点所持有的线程是否与当前线程相同return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());
}

非公平锁NonfairSync#tryAcquire

protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {//通过CAS更改state的状态值if (compareAndSetState(0, acquires)) {//把当前线程设置为锁的持有者setExclusiveOwnerThread(current);return true;}}//如果锁持有者的线程是当前线程,则可放行,锁的重入else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
} 


对比后发现,公平锁先判断是否有老祖宗节点,如果有则返回false;如果当前线程对应的node就是老祖宗节点,则直接去修改state状态,把state改为非0。

addWaiter

获取锁成功的线程去执行业务逻辑了,获取锁失败的线程则会在队列中排队等候,每个等候的线程也都不安分的。

private Node addWaiter(Node mode) {//把当前线程封装为一个Node节点Node node = new Node(Thread.currentThread(), mode);Node pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}//加入到队列的尾部enq(node);return node;
}
  1. 把当前线程封装为一个Node节点
  2. 当第一次执行这个方法时,由于head和tail都还没有赋值,则pred指向的tail也是空,所以直接直到enq(node)
  3. 当pred指向的tail不为空时,则通过CAS的方式加入到尾部,如果成功直接返回;如果失败,则进入enq(node)通过自旋的方式加入。
//通过自旋的方式将节点加入到节点的尾部
private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}
}

为了操作链表的方便,一般都要在链表的头前加入一个傀儡节点,AQS的链表也不例外。
先创建一个傀儡节点,并把head、tail均指向它,然后再把node节点加入到尾部后面,移动tail的指向。

acquireQueued

当节点成功加入到链表的尾部后,等待被唤醒,然后通过自旋的方式去获取锁

final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {//当前节点的前置节点final Node p = node.predecessor();//如果前置节点是傀儡节点(head指向傀儡节点),则再次尝试去获取锁if (p == head && tryAcquire(arg)) {//获取成功后,则移除之前的傀儡节点,head指向当前node,setHead(node);p.next = null; // help GCfailed = false;return interrupted;}//获取锁失败后,设置node节点的状态,并挂起当前节点if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}
}
  1. 获取node节点的前置节点,如果前置节点是head,则再次尝试去获取锁
  2. 设置当前node节点的前置节点状态为-1(表示后续节点正在等待状态,默认是0),然后通过自旋的后会进行到parkAndCheckInterrupt挂起当前节点
  3. LockSupport.park(this)执行完事,当前线程会一直阻塞到这个地方
  4. 当前唤醒时再次从1开始执行

AQS释放锁过程

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

主要是恢复state的值、重置锁持有都线程,然后唤醒挂起的线程。

protected final boolean tryRelease(int releases) {int c = getState() - releases;//当前线程与锁持有者线程不一样会报错if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {//重入的次数为0时,则当前线程已经没有重入了,可以清空锁的持有者free = true;setExclusiveOwnerThread(null);}setState(c);return free;
}

恢复state状态的值,如果重入次数为0时,则清空锁的持有都为null

private void unparkSuccessor(Node node) {int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);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)//唤醒下个node对应的线程LockSupport.unpark(s.thread);}

设置head指向的node节点的watiStatus的状态值,然后找到下个节点对应的线程并唤醒。

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

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

相关文章

CentOS下Samba文件服务器的安装与配置

前言&#xff1a;文件服务器提供的服务在大多数公司或企业都会被用到&#xff0c;因为在任何的公司或企业都涉及不同职位获取不同资源文件的情况&#xff0c;这就需要根据不同职位配置相关的不同权限&#xff0c;以保证相关资源文件的安全性和保密性。一、Samba介绍&#xff1a…

Java基础软件的安装及配置及Javascript的运行

1.Jdk的安装及环境变量配置&#xff1a; &#xff08;1&#xff09;计算机-属性-高级系统设置。 &#xff08;2&#xff09;环境变量-系统变量-输入变量名JAVA_HOME-输入变量值C:\Program Files (x86)\Java\jdk1.7.0_79&#xff08;jdk安装路径&#xff09; &#xff08;3&…

【 Grey Hack 】万金油脚本:在路由器上获取shell

目录脚本源码用法效果及示例版本&#xff1a;Grey Hack v0.7.3618 - Alpha 脚本源码 if params.len ! 2 or params[0] "-h" or params[0] "--help" then exit("<b>Usage: "program_path.split("/")[-1]" [ip_address] […

面试问题整理笔记系列 一 Java容器类

虚线框表示接口&#xff1b;实线框表示实体类&#xff1b;粗线框表示最常用的实体类&#xff1b;虚线箭头表示实现了这个接口&#xff1b;实现箭头表示类可以制造箭头所指的那个类的对象。 Collection&#xff1a;只允许在每一个位置上放一个对象。它包括“以一定顺序持有一组对…

【 Grey Hack 】反向Shell

目录调查准备反向shell反向shell提权版本&#xff1a;Grey Hack v0.7.3618 - Alpha 如图&#xff0c;本案例中目标IP尚未开放常见端口 调查 通过路由器获得目标PC的用户邮箱账号和相应的Password 所用脚本介绍&#xff1a; routerpsw 准备反向shell 在本机获得root后配置r…

阈值PSI代码

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

【 Grey Hack 】万金油脚本:原地提权工具

目录脚本源码用法效果及示例版本&#xff1a;Grey Hack v0.7.3618 - Alpha 脚本源码 metaxploit include_lib("/lib/metaxploit.so") if not metaxploit thenmetaxploit include_lib(current_path "/metaxploit.so") end if if not metaxploit then ex…

android之PackageManager简单介绍

PackageManager相关 本类API是对全部基于载入信息的数据结构的封装&#xff0c;包含下面功能&#xff1a; 安装&#xff0c;卸载应用查询permission相关信息 查询Application相关信息(application&#xff0c;activity&#xff0c;receiver&#xff0c;service&#xff0c;prov…

【 Grey Hack 】万金油脚本:常见端口修改Password

目录脚本源码用法效果及示例版本&#xff1a;Grey Hack v0.7.3618 - Alpha 适用于SSH (22) 端口、FTP (21) 端口、HTTP (80) 端口、SMTP (25) 端口及3306/3307 端口等。 脚本源码 if params.len ! 2 or params[0] "-h" or params[0] "--help" then exi…

JavaScript中的原型和对象机制

1 对象相关的一些语言特性1.1 一切皆为对象JavaScript里所有的东西都是对象. 对象是属性的集合. 数字, 字符串, 布尔值等原始值是"伪对象", 它们同样拥有属性, 但是是在栈上分配并按值传递. 而其他的对象是堆上分配并按引用传递.一个很重要的概念是, 函数也是对象, 能…

【 Grey Hack 】记一次被黑经历

目录又被搞了版本&#xff1a;Grey Hack v0.7.3618 - Alpha 胆大包天的我黑进游戏内shop的IP后&#xff0c;顺着其上面的日志溯源到不少疑似其他玩家租的服务器&#xff0c;暂时没什么进展 不久后回到桌面才发现自己已经被黑入了 随后我打开日志查看记录 只看清是从我的1222…

【 Grey Hack 】加强版nmap

目录probe使用方法效果routerpcscan使用方法效果版本&#xff1a;Grey Hack v0.7.3618 - Alpha probe if params.len ! 1 or params[0] "-h" or params[0] "--help" then exit(command_info("<b>probe [IP]</b>")) if not is_va…

文档容器iOS网络编程-iCloud文档存储编程实例

在本文中,我们主要绍介文档容器的容内,自我感觉有个不错的建议和大家分享下 iCloud文档存储程编对相键值据数存储而言比较复杂&#xff0c;涉及到自定义文档类、取得iCloud文档录目、找查Ubiquity容器中的文档、保存文档和决解文档冲突等容内。 实例&#xff1a;iCloud文档存储…

CSS3之伪元素选择器和伪类选择器

伪类选择器&#xff0c;和一般的DOM中的元素样式不一样&#xff0c;它并不改变任何DOM内容。只是插入了一些修饰类的元素&#xff0c;这些元素对于用户来说是可见的&#xff0c;但是对于DOM来说不可见。伪类的效果可以通过添加一个实际的类来达到。 a:link|a:visited|a:hover|a…

CSS只是进化的一部分

Bert Bos是一位计算机科学家&#xff0c;他也是CSS的创始人之一。在CSS的发展过程中&#xff0c;Bos是最早与Hkon Wium Lie&#xff08;CSS之父&#xff09;合作的人之一。在1996年&#xff0c;他加入了World Wide Web Consortium&#xff08;W3C&#xff09;负责CSS的开发。他…

【 Grey Hack 】WIFI万能钥匙

目录脚本源码使用方法效果版本&#xff1a;Grey Hack v0.7.3618 - Alpha 脚本源码 computer get_shell.host_computer status "Unknown Error." crypto include_lib("/lib/crypto.so") if not crypto thencrypto include_lib(current_path "/cr…

面向对象编程学习5月7日-5月23日 网络直播yii-外企使用最多的PHP框架

每日一贴,今天的内容关键字为面向对象编程学习 课程说明: -------------------------------------------------------------------------------------------------------------- 9天玩转大型php框架Yii&#xff08;即将开始&#xff09;&#xff08;如何加入费免公然讲堂&#…

PHP 四种基本排序算法的代码实现(2)

2019独角兽企业重金招聘Python工程师标准>>> 许多人都说算法是程序的核心&#xff0c;算法的好坏决定了程序的质量。作为一个初级phper&#xff0c;虽然很少接触到算法方面的东西。但是对于基本的排序算法还是应该掌握的&#xff0c;它是程序开发的必备工具。这里介…

【 Grey Hack 】综合工具 shellOs

目录交互界面基本功能本地攻击攻击本机远程攻击应用程序扫描功能更新IP并扫描深度扫描黑入功能类shell交互界面一些特殊命令bouncerunScanPswvimbuild脚本源码版本&#xff1a;Grey Hack v0.7.3619 - Alpha 交互界面 基本功能 使用cd命令和数字选项切换路径 本地攻击 攻击本…

HIVE 一行转多行输出办法

2019独角兽企业重金招聘Python工程师标准>>> 1.问题描述 hive UDF 格式&#xff1a;oc号1_an号1_次数;oc号2_an号2_次数 转换结果&#xff1a; oc号1 an号1 次数 oc号2 an号2 次数 一行转多行&#xff0c;一行转多列 2.实现思路 第一步分割&#xff1a;split(oc号…