一、定义
-
抽象的队列同步器
-
锁和其他同步器组件的基石
-
ReentrantLock、CountDownLatch、Semaphore
- 内含静态内部类sync继承AQS
- 加锁和解锁底层都是通过AQS实现
-
二、基本结构
-
属性
- volatile int state(代表共享资源):0=空闲,1=锁占有
-
FIFO的双向队列
-
CLH队列
- 单向链表
-
AQS队列是CLH变体的head,tail节点的虚拟双向链表
- head和tail都是虚节点
-
线程封装成Node节点
-
Node里面包含内部变量volatile int waitStatus+前后节点对象
-
waitStatus表示节点在队列中的状态
-
CANCELLED=1
- 表示线程取消了等待
-
SIGNAL=-1
- 表示后续节点需要被唤醒
-
通过waitStatus小于等于0,来判断是否是CANCELLED状态
-
-
-
三、加锁
-
new ReentrantLock().lock()
-
非公平锁 NonfairSync#lock()
-
compareAndSetState(0, 1) 通过 CAS 的方式尝试将 state 从0改为1
- true,获取锁成功,将当前线程设置为独占线程,结束
-
获取锁失败调用acquire(1)
- tryAcquire 再次尝试获取锁资源,如果尝试成功,返回true,尝试失败返回false
获取state,判断如果等于0,通过cas尝试获取锁;否则判断线程如果是当前占有锁线程,state+1可重入,都不是返回fasle获取锁失败
-
尝试获取锁资源失败,将当前线程封装成一个Node,追加到AQS的队列中
- addWaiter() 将当前线程封装成Node节点加到队列尾部
-
acquireQueued()将已经在队列中的node尝试去获取锁否则挂起。
获取当前线程节点的上一节点,如果为头节点(头节点没有意义),说明当前节点是head后的第一个节点,再次尝试获取锁,成功将当前节点设置为头节点
- 如果p不是head节点或者没有拿到锁资源执行shouldParkAfterFailedAcquire()
获取上一节点的等待状态,如果为-1直接返回true
执行parkAndCheckInterrupt()方法,通过LockSupport.park()方法阻塞当前线程。等以后执行unpark方法的时候,会继续acquireQueued()死循环获取锁逻辑,直到获取锁资源并结束死循环。
大于0说明上一节点已失效,剔除,一直向前查找有效节点,直到找到第一个ws<=0的节点为止,将node节点挂到该节点后面
小于0,通过cas将等待状态设置为-1
返回false,继续死循环获取锁
四、解锁
- new ReentrantLock().unlock()
- tryRelease尝试释放锁资源,如果释放成功,把AQS队列的节点用unpack()唤醒
tryRelease作用将state置为0
- unparkSuccessor()用于唤醒AQS中被挂起的线程。