AQS是什么?它的应用场景在哪?它跟CountDownLatch和ReentrantLock这些类又有什么关系?(附流程图)
0x01 是什么
AbstractQueuedSynchronizer翻译过来就是抽象队列同步器
AQS是JUC包中的核心类,主要是一个抽象队列同步器,JUC包中的所有锁相关基本都是用它实现的。
AQS中有两个核心的概念CAS、CLH队列
- CAS:即Compare and Swap,翻译过来是
比较和交换,CAS是一种乐观锁,原理是调用Unsafe类提供的一些native方法来硬件级别的原子操作(调用C++编写的HotSpot代码); - CLH队列:非阻塞的 FIFO 队列,通过自旋锁和 CAS 保证节点插入和移除的原子性,实现无锁快速插入,主要作用就是存放竞争失败的线程并且使用
Unsafe.park()挂起线程;
0x02 怎么用
AQS是一个抽象类,只提供了一些基础的方法,无法直接使用。需要子类实现他来实现不同逻辑的锁功能。JUC中常见的ReentrantLock、CountDownLatch等都是用它实现的,他们提供了一个内部类继承了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,请配合源码一起食用,新标签中打开图片看大图)
