AQS之acquire方法
前言
JUC(java.util.concurrent)包下的很多并发同步工具类,大多基于AQS(AbstractQueuedSynchronizer 抽象队列同步器)实现加解锁的逻辑,其中acquire()尤为重要。
① acquire()是独占式地获取资源,acquireShared()是共享式地获取资源,如ReentrantReadWriteLock的writeLock和readLock;
② AQS采用模板方法模式实现acquire(),封装了线程获取资源失败后,进入同步队列并阻塞的逻辑。
三个核心方法
1. tryAcquire(int arg)
2. addWaiter()
3. acquireQueued(final Node node, int arg)
public final void acquire(int arg) {//尝试获取资源失败,且成功加入同步队列,则阻塞线程if (!tryAcquire(arg) &&acquireQueued(addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg))selfInterrupt();
}
1. tryAcquire(int arg)
① 尝试获取资源,留给子类重写,否则会抛出异常;
② 尝试成功则修改资源状态及拥有者,尝试失败则返回false。
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}
2. addWaiter()
① 将当前线程封装成Node节点,并添加到同步队列尾部;
② 队列未初始化时,pred和tail都为null,tail为null时执行enq();
③ compareAndSetTail(pred, node)是采用cas线程安全地设置队列尾节点。
private Node addWaiter(Node mode) {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;
}
2.1 enq(final Node node)
① 通过cas自旋的方式将节点添加到队列尾部;
② tail为null表示同步队列未初始化,即没有虚拟head节点;
③ 所以在没有虚拟节点的情况下,先走if逻辑添加虚拟节点,再走else逻辑的。
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;}}}
}
3. acquireQueued(final Node node, int arg)
该方法是将获取资源失败的线程加入到同步队列中,并阻塞线程。
① 如果前置节点为头结点且成功获得资源,则将当前节点设置为头结点;
② 前置节点非头节点的线程,在获取资源失败后需要阻塞;
③ 阻塞线程并检查是否中断,中断则退出同步队列。
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)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}// ②阻塞线程if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}
}
3.1 shouldParkAfterFailedAcquire(Node pred, Node node)
① 判断前节点的ws是否大于0,大于0表示节点被取消(因中断等异常原因)
② 如果前节点被取消,则一直往前找,直到找到不被取消的节点
③ 将前节点的ws设置为SIGNAL,表示下一个节点需要被唤醒
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;if (ws == Node.SIGNAL)return true;if (ws > 0) {do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;
}
3.2 parkAndCheckInterrupt()
① LockSupport调用UNSAFE类的unpark()阻塞当前线程
private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();
}
总结
并发工具类调用lock()方法进行加锁操作时,底层采用了acquire()进行资源获取。首先,会尝试获取资源(交给子类实现),获取资源失败则封装成Node节点,放入同步队列中并阻塞挂起,同时检测是否中断,中断则取消排队。
AQS之acquire方法相关推荐
- 【java并发】AQS中acquire方法解析
AQS,全名AbstractQueuedSynchronizer(抽象队列同步器),它是CLH(不明白的可以先了解一下CLH)的变种.它与CLH不同之处在于: CLH是一种公平锁,它是通 ...
- AQS的核心方法-acquire()解析
以下是AQS的acquire()方法源码: public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueu ...
- ReentrantLock acquire方法源码解析
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCL ...
- acquire方法_Python锁类| 带有示例的acquire()方法
acquire方法 Python Lock.acquire()方法 (Python Lock.acquire() Method) acquire() is an inbuilt method of t ...
- 通过acquire方法看懂RateLimiter限流机制
通过acquire方法看懂RateLimiter限流机制 关键方法 1)resync,动态计算剩余令牌数量和下次发放时间: 2)reserveEarliestAvailable,预定令牌,允许超发,超 ...
- AQS核心流程解析-acquire方法
该方法是一个模板方法,用于线程获取[锁] public final void acquire(int arg) {if (!tryAcquire(arg) && acquireQueu ...
- java中acquire()_Java高并发系列之AQS中acquire源码解析
我们知道,AQS中最重要的两个方法就是acquire和release方法.我们本文来走读走读acquire的源码. 首先,tryAcquire是需要子类具体去实现,其作用就是设置state的值,如果设 ...
- 关于AQS中enq( )方法CAS操作的疑惑
private Node enq(final Node node) {for (;;) {Node t = tail;//如果队列为空则新建头结点if (t == null) { // Must in ...
- 从ReentrantLock的实现看AQS的原理及应用
来自:美团技术团队 AQS作为JUC中构建锁或者其他同步组件的基础框架,应用范围十分广泛,这篇文章会带着大家从可重入锁一点点揭开AQS的神秘面纱. 前言 Java中的大部分同步类(Lock.Semap ...
最新文章
- HDU 1090 A+B for Input-Output Practice (II)
- 机器学习中的L1与L2正则化图解!
- 2018-07-12 第六十七天 EsayUI
- pycharms怎么看文件被什么引用_误删文件咋办?看我怎么起死回生……
- 有一种道理叫“实践”
- C++中继承的基本概念
- pymysql操作mysql数据库
- 假设有搅乱顺序的一群儿童成一个队列_数据结构与算法系列之栈amp;队列(GO)...
- 让你的原创设计作品展示给世界
- 浅谈UWB室内定位(二)_vortex_新浪博客
- 单片机的各种存储的含义和区别
- 《基因大数据智能生产及分析》笔记
- 无需编码 9款优秀的数据地图可视化工具平台
- 《又到毕业季》MATLAB GUI 基础控件与交互
- Word 远程调用失败:异常来自 HRESULT:0x800706BE
- 计算机能使用硬盘吗,旧电脑的硬盘能直接插在新电脑上用吗?
- ESP32配置mqtt arduino
- 【机械仿真】基于matlab GUI曲柄摇杆机构运动仿真【含Matlab源码 1608期】
- 同一局域网下 macOS 和 windows 电脑 如何快速共享文件
- EasyUi 快速入门