并发编程学习笔记(二十四、AQS总结)

目录:

  • AQS整体介绍
  • AQS类图
  • AQS实现

AQS总结:

AQS及其实现类已经学完了,今天我就来按照自己的理解简单的总结下AQS。

首先AQS全称为AbstractQueuedSynchronize,从含义上可以看出它是队列同步器,是用于构建锁其它同步组件基础框架

AQS整体介绍:

AQS这个基础框架提供了共享锁排它锁的实现,并且这两种锁都具有获取锁释放锁两种功能;以及AQS中比较重要的ConditionObject、Node。

1、共享锁:

  • 获取锁:java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireShared
  • 释放锁:java.util.concurrent.locks.AbstractQueuedSynchronizer#releaseShared

2、排它锁:

  • 获取锁:java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
  • 释放锁:java.util.concurrent.locks.AbstractQueuedSynchronizer#release

3、ConditionObject(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject):条件锁的实现类,类似于Object中wait、notify。

4、Node:实现AQS的基础数据结构,基于链表实现

AQS类图:

1、AQS直接实现类:

AQS默认的直接实现类有三个,分别是ReentrantLock、Semaphore、CountDownLatch,这三个实现类都是通过内部类Sync来实现AQS的。

其中ReentrantLock、Semaphore实现了公平锁和非公平锁。

2、间接实现类:

从图中可以看出AQS间接实现类都是基于ReentrantLock和Condition实现的,其中Condition的默认实现就是AQS中的ConditionObject

AQS实现:

AQS基于双向链表实现,内部获取锁、释放锁采用模板模式,具体的获取释放逻辑交给子类实现,极大的增加了框架的灵活度

1、双向链表:

 1 public abstract class AbstractQueuedSynchronizer
 2     extends AbstractOwnableSynchronizer
 3     implements java.io.Serializable {
 4     
 5     /**
 6      * 队列节点数据类
 7      */
 8     static final class Node {
 9         /** 前驱节点 */
10         volatile Node prev;
11         /** 后继节点 */
12         volatile Node next;
13         /** 下一个等待节点,condition模式下使用 */
14         Node nextWaiter;
15     }
16 
17     /** 头结点 */
18     private transient volatile Node head;
19     /** 尾结点 */
20     private transient volatile Node tail;
21 
22 }

2、模板模式:

 1 public final void acquire(int arg) {
 2     if (!tryAcquire(arg) &&
 3         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
 4         selfInterrupt();
 5 }
 6 
 7 protected boolean tryAcquire(int arg) {
 8     throw new UnsupportedOperationException();
 9 }
10 
11 public final boolean release(int arg) {
12     if (tryRelease(arg)) {
13         Node h = head;
14         if (h != null && h.waitStatus != 0)
15             unparkSuccessor(h);
16         return true;
17     }
18     return false;
19 }
20 
21 protected boolean tryRelease(int arg) {
22     throw new UnsupportedOperationException();
23 }

可以看出排它锁的获取锁、释放锁的获取释放逻辑都抛出了异常,它会交给子类实现;共享锁也是如此,这里不再赘述。

——————————————————————————————————————————————————————————————————————

因为之前章节已经详述过具体实现逻辑了,这里就不再细说,我直接总结下流程:

1、排它锁:

  • 获取锁:java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
    • 获取成功:直接结束acquire方法,去执行线程内部逻辑。
    • 获取失败:
      • 通过addWaiter方法入队,以CAS的方式进入队尾。
        • CAS成功,直接入队尾。
        • CAS失败,通过自旋的方式一直CAS直到入队成功。
      • 入队后addWaiter返回入队节点Node,并执行acquireQueued函数。
        • 若传入acquireQueued函数的Node的前驱节点为头节点就会尝试获取锁,获取成功后返回中断标识。
        • 若非上述情况,Node的前驱节点不为头节点或获取锁失败了,就会调用shouldParkAfterFailedAcquire与parkAndCheckInterrupt来维护中断标识。
        • shouldParkAfterFailedAcquire:检查并更新已经取消了的线程,如果线程需要被阻塞则返回true,否则返回false。

  • 释放锁:java.util.concurrent.locks.AbstractQueuedSynchronizer#release
    • 释放成功:
      • 头节点状态为0,方法直接返回true。
      • 头节点状态不为0,调用unparkSuccessor方法,改方法用于唤醒头节点的后继节点,并且还会清理已取消的线程。
      • 从函数可以看出释放锁,只会释放头节点的,因为队列也是先进先出的嘛。
    • 释放失败:方法返回false。

2、共享锁:可参考https://www.cnblogs.com/bzfsdr/p/13141361.html中的共享锁和独占锁在源码上有何区别

3、ConditionObject:最核心的逻辑就是等待用LockSupport.park(),唤醒用LockSupport.unpark();然后就是条件队列用的是nextWaiter字段,同步队列是用prev、next记住这点就好了,其它的大同小异,当然有些特殊的处理逻辑还需要你自己取琢磨。

  • 线程等待:java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject#await()
    • 若线程已经中断,则抛出InterruptedException(响应中断标识)。
    • 将当前线程加入到条件队列中(设置Node节点的nextWaiter属性)。
    • 释放锁,调用release()方法成功后则释放,否则抛出非法的锁状态异常(因为进await()方法时并没有判断当前线程是否持有这把锁)。
    • 当当前线程为条件队列时,调用LockSupport.park()锁住ConditionObject对象,会在调用signal时是否锁,并往后执行。
    • 之后就是一些异常流程的处理。
  • 线程唤醒:java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject#signal()
    • 判断当前线程是否持有锁,若未持有则抛出IllegalMonitorStateException。
    • 将条件队列上所有节点清空(断开nextWaiter),并自旋的加入条件队列后执行LockSupport.unpark()解锁。
原文地址:https://www.cnblogs.com/bzfsdr/p/13245800.html