• 并发编程学习笔记(二十四、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()解锁。
  • 相关阅读:
    spsss基本统计分析操作攻略
    MATLAB读取Excel表格数据和处理数据
    MATLAB 雷达图画图函数
    spss新手教程
    MATLAB绘制饼状图
    高斯消元法解线性方程组(C++实现)
    Java和matlab混合编程
    Matlab与Java混合编程的教程
    两组数据的相关性分析
    vs2013+QT5环境
  • 原文地址:https://www.cnblogs.com/bzfsdr/p/13245800.html
Copyright © 2020-2023  润新知