本文共 3045 字,大约阅读时间需要 10 分钟。
上一篇文章 对 AbstractQueuedSynchronizer
(AQS) 的相关 概念 和 作用 作了简要介绍, 本文将继续对 AQS 做进一步介绍 (基于 jdk1.8 源码)。
阅读之前需要了解什么是 共享锁 和 独占锁 .
上一篇有提到 CountDownlatch, ReentrantLock, ReentrantReadWriteLock, Semaphore
等并发包工具类都是基于 AQS 实现的, 这要求 AQS 需要提供很 丰富 + 通用 的功能,
比如:
ReentranLock
是独占锁, 而 ReentrantReadWriteLock
是共享锁, 他们分别调用 AQS 的 acquire
方法去获取独占资源, acquireShared
方法去获取共享资源。但是又共同调用了 compareAndSetState
方法去实现 CAS 操作。
这还只是这两个类的之间的区别, 而 java.util.concurrent
包下面有诸多工具类,
因此, 我们暂时没必要全面了解 AQS的所有功能
那么, 接下来就着重分析 AQS 在 ReentrantLock
的作用。
先来看一张 AQS 的类图
AbstractOwnableSynchronizer
ConditionObject
和 Node
对于研究 ReentrantLock
, 我们暂时只需要关注 Node 类, 和 AQS 中的部分 属性 和 方法
Node
的所有属性 prev
和 next
分别为前驱、后继指针thread
存放的是线程对象SHARED
标记当前线程是因为抢夺共享资源,而被放入 AQS 队列的EXCLUSIVE
标记当前线程是因为抢夺独占资源, 而被放入 AQS 队列的waitStatus
表示当前线程等待状态, 可选值为: CANCELLED
线程被取消了CONDITION
线程在条件队列中等待SIGNAL
线程需要被唤醒PROPAGATE
释放共享资源时, 需要通知其他节点上一篇文章 有提到, AQS 是一个双向队列, 队列中的元素就是 Node
对象
// 指向队列的头 private transient volatile Node head; // 指向队列的尾 private transient volatile Node tail; // 上一篇也有讲到在 ReentrantLock 中的作用,表示重入次数 private volatile int state;
acquire(int arg)
release(int arg)
compareAndSetState(long expect, long update)
获取独占资源
该方法大致的逻辑是:
尝试去获取锁, 如果获取锁失败,就把当前线程放入队列, 并把当前线程挂起
public final void acquire(int arg) { // tryAcquire 尝试去获取锁, 而 !tryAcquire 则表示获取锁失败 // Node.EXCLUSIVE 上面有讲到是标记当前线程是获取独占资源失败 // addWaiter 放入 AQS 队列 // acquireQueued 这里可以先简单理解为 阻塞住了 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) { selfInterrupt(); }}
其中 tryAcquire, addWaiter, acquireQueued
三个方法内部具体逻辑, 下一篇文章会继续深入讲解。
释放独占资源
该方法大致的逻辑是:
当一个线程调用 release
方法时, 会调用 tryRelease
尝试释放资源(通过设置 state
属性), 然后通过unparkSuccessor
最终调用 LockSupport.unpark
方法去激活 AQS 队列中的被阻塞的线程。
public final boolean release(long arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
上一篇有提到 AQS 是一个抽象类, tryAcquire 和 tryRelease
两个方法在这个抽象类中并没有具体实现。它们是交给具体的子类来实现的,。
在ReentrantLock
中, 当 state
为 0 时,表示锁未被占用, 为 1 时,表示被占用;
tryAcquire
方法, 会使用 CAS 操作判断当前的 state
是否为 0, state
设为 1, 并设置当前锁的拥有者为当前线程, 然后返回 true相反的, ReentrantLock
内部在实现tryRelease
方法时, 会使用 CAS 操作把 state
的值从 1 修改为 0, 并设置当前锁的拥有者为 null, 然后返回 true, 如果 CAS 操作失败,就返回 false.
该方法直接调用了 unsafe
对象的 native 方法, 提供了 CAS 操作, 这一点在上一篇文章 中也有提到。
protected final boolean compareAndSetState(long expect, long update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapLong(this, stateOffset, expect, update); }
ReentrantLock, CountDownlath
等并发工具类有其各自的作用, 而 AQS 是实现这些不同作用的基础,。
AQS 本质上是一个 双向队列, 当多个线程通过 ReentrantLock 对象的 lock()方法
同时去 尝试抢占锁 时, 未成功获取锁的线程会被放入 AQS 队列 中去排队等待,
而尝试去抢占锁的方法 tryAcquire
是一个抽象方法, 由继承 AQS 的子类去自行实现, tryAcquire
内部通常会通过 CAS 操作去判断获取锁的结果。
转载地址:http://jgpdz.baihongyu.com/