前言

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方法相关推荐

  1. 【java并发】AQS中acquire方法解析

    AQS,全名AbstractQueuedSynchronizer(抽象队列同步器),它是CLH(不明白的可以先了解一下CLH)的变种.它与CLH不同之处在于:        CLH是一种公平锁,它是通 ...

  2. AQS的核心方法-acquire()解析

    以下是AQS的acquire()方法源码: public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueu ...

  3. ReentrantLock acquire方法源码解析

    public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCL ...

  4. acquire方法_Python锁类| 带有示例的acquire()方法

    acquire方法 Python Lock.acquire()方法 (Python Lock.acquire() Method) acquire() is an inbuilt method of t ...

  5. 通过acquire方法看懂RateLimiter限流机制

    通过acquire方法看懂RateLimiter限流机制 关键方法 1)resync,动态计算剩余令牌数量和下次发放时间: 2)reserveEarliestAvailable,预定令牌,允许超发,超 ...

  6. AQS核心流程解析-acquire方法

    该方法是一个模板方法,用于线程获取[锁] public final void acquire(int arg) {if (!tryAcquire(arg) && acquireQueu ...

  7. java中acquire()_Java高并发系列之AQS中acquire源码解析

    我们知道,AQS中最重要的两个方法就是acquire和release方法.我们本文来走读走读acquire的源码. 首先,tryAcquire是需要子类具体去实现,其作用就是设置state的值,如果设 ...

  8. 关于AQS中enq( )方法CAS操作的疑惑

    private Node enq(final Node node) {for (;;) {Node t = tail;//如果队列为空则新建头结点if (t == null) { // Must in ...

  9. 从ReentrantLock的实现看AQS的原理及应用

    来自:美团技术团队 AQS作为JUC中构建锁或者其他同步组件的基础框架,应用范围十分广泛,这篇文章会带着大家从可重入锁一点点揭开AQS的神秘面纱. 前言 Java中的大部分同步类(Lock.Semap ...

最新文章

  1. HDU 1090 A+B for Input-Output Practice (II)
  2. 机器学习中的L1与L2正则化图解!
  3. 2018-07-12 第六十七天 EsayUI
  4. pycharms怎么看文件被什么引用_误删文件咋办?看我怎么起死回生……
  5. 有一种道理叫“实践”
  6. C++中继承的基本概念
  7. pymysql操作mysql数据库
  8. 假设有搅乱顺序的一群儿童成一个队列_数据结构与算法系列之栈amp;队列(GO)...
  9. 让你的原创设计作品展示给世界
  10. 浅谈UWB室内定位(二)_vortex_新浪博客
  11. 单片机的各种存储的含义和区别
  12. 《基因大数据智能生产及分析》笔记
  13. 无需编码 9款优秀的数据地图可视化工具平台
  14. 《又到毕业季》MATLAB GUI 基础控件与交互
  15. Word 远程调用失败:异常来自 HRESULT:0x800706BE
  16. 计算机能使用硬盘吗,旧电脑的硬盘能直接插在新电脑上用吗?
  17. ESP32配置mqtt arduino
  18. 【机械仿真】基于matlab GUI曲柄摇杆机构运动仿真【含Matlab源码 1608期】
  19. 同一局域网下 macOS 和 windows 电脑 如何快速共享文件
  20. EasyUi 快速入门

热门文章

  1. 画矢量图上传iconfont并使用
  2. vs2019 编译 openssl 1.0.2
  3. AUTOCAD_ELECTRICAL从入门到放弃(二)导线的绘制与连接
  4. 如何使用计算机办公软件,如何正确使用电脑办公软件?你肯定不知道这些?
  5. 在程序运行时,我们遇到的200,404,500分别代表什么!
  6. 醒悟的日子,我是怎么一步一步走向程序员道路的
  7. 数学建模——运输问题,你会的还只有初级工程师的技术吗
  8. SPSS-多重响应分析
  9. 倍加福传感器UC6000-30GM70-UE2R2-V15
  10. 安卓手机远程操作win8.1,RD Client设置教程