理解JUC包中的AQS到底是什么

AQS是什么?它的应用场景在哪?它跟CountDownLatch和ReentrantLock这些类又有什么关系?(附流程图)

0x01 是什么

AbstractQueuedSynchronizer翻译过来就是抽象队列同步器

AQS是JUC包中的核心类,主要是一个抽象队列同步器,JUC包中的所有锁相关基本都是用它实现的。
AQS中有两个核心的概念CASCLH队列

  • CAS:即Compare and Swap,翻译过来是比较和交换,CAS是一种乐观锁,原理是调用Unsafe类提供的一些native方法来硬件级别的原子操作(调用C++编写的HotSpot代码);
  • CLH队列:非阻塞的 FIFO 队列,通过自旋锁和 CAS 保证节点插入和移除的原子性,实现无锁快速插入,主要作用就是存放竞争失败的线程并且使用Unsafe.park()挂起线程;

0x02 怎么用

AQS是一个抽象类,只提供了一些基础的方法,无法直接使用。需要子类实现他来实现不同逻辑的锁功能。JUC中常见的ReentrantLockCountDownLatch等都是用它实现的,他们提供了一个内部类继承了AQS实现自己的不同逻辑。

0x03 执行流程

先用大白话写一遍,然后请按照下面的流程图(最好一起看源码),一起理解;

获取锁成功的情况:

首先调用tryAcquire的时候会判断state == 0?是否等于0,等于0说明没有线程占用,直接设置拥有线程=Thread.currentThread()获取锁成功继续执行代码;

尝试获取锁失败,判断是否重复

如果尝试获取锁state == 0?失败,则判断current == getExclusiveOwnerThread()当前线程是否等于已经持有的线程,择state+=1,继续执行,最后调用unlock释放锁的时候-1;

尝试获取锁失败,进入队列

如果尝试获取锁state == 0?失败 && 也不是当前线程重入,择先执行addWaiter(Node.EXCLUSIVE)(EXCLUSIVE表示当前节点正在独占模式下等待)
if(tail != null)最后一个节点不为空,则将放入尾节点的后面,把当前线程变成尾节点;
else最后一个节点空,说明整个队列都是空的,执行enq(node)里面有一个自旋锁,初始化节点并把当前节点置于next;

然后执行acquireQueued(上面addWaiter返回的node),这里又是一个自旋锁;
if (p == head && tryAcquire(arg))判断上一个节点是否是头结点,是头节点就直接设置成head,获取锁,返回;

else执行shouldParkAfterFailedAcquire(p, node),又一个自旋流程

  • 分支1 上一个节点的状态 == Node.SIGNAL(表示上一个节点正在等待unpark)直接返回true,然后将当前线程park;
  • 分支2 上一个节点的状态 > 0判断前置节点的状态,如果是取消状态就直接忽略,找到前面的挂起节点设置成当前节点的prev;
  • 分支3 上一个节点<=0&&ws!=1 择用CAS将上一个节点设置成Node.SIGNAL,返回false,通过自旋再次进入这个大循环,进入分支1,然后返回true,将当前线程park;

流程图(画了2个小时0_0,请配合源码一起食用,新标签中打开图片看大图)

aqs-img.png