当前位置:网站首页>【无标题】
【无标题】
2022-07-01 08:15:00 【今天学不学?】
AQS
AQS在java并发中的地位,相当于JVM在java中的地位,他就是抽象阻塞队列同步器,是实现同步器和锁的基石,就是base父类
抽象类:相当于base父类,可以是很多类的模板框架,组件,在抽象类的子类有很多情况
队列:用于锁的争抢,没抢到资源的到阻塞队列排队
概述:用于实现锁和其他同步器组件的公共基础部分的抽象实现,是重量级的基础框架以及JUC体系的基石,就是一个抽象FIFO队列来完成资源获取线程的阻塞队列和一个int变量的状态来表示当前资源是否被占用
Java中的大部分同步类(Lock、Semaphore、ReentrantLock等)都是基于AbstractQueuedSynchronizer(简称为AQS)实现的。AQS是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。
在AQS中的锁类型有两种:分别是**「Exclusive(独占锁)**「和」**Share(共享锁)」**。
「独占锁」就是「每次都只有一个线程运行」,例如ReentrantLock。
「共享锁」就是「同时可以多个线程运行」,如Semaphore、CountDownLatch、ReentrantReadWriteLock。名称为AbstractQueueSynchronized,是阻塞式锁和相关的同步器工具的框架
概括:整体上就是一个抽象的FIFO队列来完成资源获取线程的排队任务,并通过一个int类变量表示持有锁的状态

AQS框架
AQS框架图

AQS内部体系框架

数据结构
AQS最基本的数据结构为Node节点

属性解释:
waitStatus:在队列中节点的状态
thread:当前节点的线程
prev:前驱节点
predecessor:返回前驱节点,没有的话抛出npe
nextWaiter:指向下一个处于CONDITION节点
next:后继指针
waitStatus属性值种类:
| 枚举 | 含义 |
|---|---|
| 0 | 当一个Node被初始化的时候的默认值 |
| CANCELLED | 为1,表示线程获取锁的请求已经取消了 |
| CONDITION | 为-2,表示节点在等待队列中,节点线程等待唤醒 |
| PROPAGATE | 为-3,当前线程处在SHARED情况下,该字段才会使用 |
| SIGNAL | 为-1,表示线程已经准备好了,就等资源释放了 |
源码分析
从ReentrantLock中的非公平锁来进行源码分析
从lock方法进行入手
ReentrantLock实现了Lock接口,里面包含了一个sync同步器,该同步器继承了AQS类,底层lock调用了sync同步器的lock锁
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
try{
//同步代码块{}
}finally {
lock.unlock();
}
}
2.sync同步器的lock方法
- 首先CAS进行判断,能否当前线程先抢到锁,如果成功,该线程直接获取资源,否则进行acquire方法尝试获取资源
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
3.acquire方法
该方法主要有三大体系
- tryAcquire:尝试获取锁,如果再次没有获取到锁,返回false,进行addWaite(Node.EXCLUSIVE)r方法的执行
- addWaiter(Node.EXCLUSIVE):如果没有获取成功,将线程加入到队列中
- 最后调用acquireQueued()方法:通过 “死循环”的方式获取同步状态
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
加入队列的时机
当执行acquire(1)时,会通过tryAcquire获取锁,在这种情况下,如果获取锁失败,就会调用addWaiter加入到等待队列中去
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();//先获取资源的状态
if (c == 0) {
//如果为0,表示该资源没有被占用,可以进行获取
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);//获取资源,返回true
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//如果获取的资源线程为当前资源线程,返回true
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
如何加入队列
获取锁失败后,执行addwaiter加入等待队列
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);//新建一个node节点
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;//取出当前尾结点
if (pred != null) {
//如果尾节点不为空 说明队列已存在阻塞线程
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);//如果尾节点为空,表示阻塞队列中还没node节点,进行入队操作
return node;
}
入队操作
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
//进行队列的初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZUKGENuu-1656424435234)(C:\Users\小刘同学\Desktop\入队操作.png)]
等待队列中线程出队时机
前驱是头结点,就获取到了同步状态。
head节点表示获取锁成功的节点,当头结点在释放同步状态时,会唤醒后继节点,如果后继节点获得锁成功,会把自己设置为头结点,节点的变化过程如下
这个过程也是涉及到两个变化
- 修改head节点指向下一个获得锁的节点
- 新的获得锁的节点,将prev的指针指向null
这里有一个小的变化,就是设置head节点不需要用CAS,原因是设置head节点是由获得锁的线程来完成的,而同步锁只能由一个线程获得,所以不需要CAS保证,只需要把head节点设置为原首节点的后继节点,并且断开原head节点的next引用即可

边栏推荐
- Redis publish subscription
- 使用beef劫持用戶瀏覽器
- Data analysis notes 11
- Rumtime 1200 upgrade: London upgrade support, pledge function update and more
- Precautions and skills in using regular expressions in golang
- Yolov5 advanced six target tracking environment construction
- [detailed explanation of Huawei machine test] judgment string subsequence [2022 Q1 Q2 | 200 points]
- Leetcode t29: divide two numbers
- golang中的正则表达式使用注意事项与技巧
- 軟鍵盤高度報錯
猜你喜欢
随机推荐
slice扩容机制分析
Hijacking a user's browser with beef
Maneuvering target tracking -- current statistical model (CS model) extended Kalman filter / unscented Kalman filter matlab implementation
【入门】输入n个整数,输出其中最小的k个
Use threejs simple Web3D effect
Thread safety analysis of [concurrent programming JUC] variables
When using charts to display data, the time field in the database is repeated. How to display the value at this time?
Soft keyboard height error
Leetcode t39: combined sum
Provincial election + noi Part III tree problems
01 numpy introduction
初学者如何正确理解google官方建议架构原则(疑问?)
Leetcode T29: 两数相除
MATLAB小技巧(16)矩阵特征向量特征值求解一致性验证--层次分析
Deep learning systematic learning
毕业论文中word的使用1-代码域标公式
leetcode T31:下一排列
[detailed explanation of Huawei machine test] judgment string subsequence [2022 Q1 Q2 | 200 points]
Learn reptiles for a month and earn 6000 a month? Tell you the truth about the reptile, netizen: I wish I had known it earlier
防“活化”照片蒙混过关,数据宝“活体检测+人脸识别”让刷脸更安全





![[untitled]](/img/be/3523d0c14d555b293673af2b6fbcff.jpg)



![[untitled]](/img/b9/6922875009c2d29224a26ed2a22b01.jpg)