AQS是什么?
AbstractQueuedSynchronizer,提供了一个框架,用于实现依赖先进先出(FIFO)等待队列的阻塞锁和相关同步器(信号量、事件等)。此类旨在为大多数依赖单个原子int值表示状态的同步器提供有用的基础。是JUC内容中最重要的基础。最主要的组成部分是一个FIFO队列和一个int类型的状态属性。
ReentrantLock是如何使用AQS实现公平和非公平锁的?
ReentrantLock实现了Lock,内部聚合了抽象类Sync,抽象类Sync实现了继承了AQS,抽象类Sync的实现FairSync和NonfairSync分别实现了公平锁和非公平锁。例如在调用lock方法时,均会进入AQS的acquire方法,接着进入tryAcquire方法(钩子,父类实现直接抛出异常,子类可以不实现),区别在于公平锁在判断里多一个判断当前线程是否在头节点或者队列是否为空的条件,来实现先进先出的公平模式。
AQS的acquire方法,lock的时候调用
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- tryAcquire:ReentrantLock实现尝试抢占锁,抢占成功直接返回
- addWaiter:抢占失败后接着执行,尝试将当前任务加入队列尾部,如果是第一个任务,为抢占成功,会加一个new node()对象作为虚拟head占位节点,实际有任务的节点是从队列里第二个node开始。!循环直到抢占到锁后,将当前任务设置为head节点,将head的next置为null,方便gc回收,此后队列中任务未执行完时,head结点的任务是当前正在执行的任务,而不是虚拟head占位节点。
- acquireQueued:继续循环,按照队列顺序尝试抢占锁,成功后则返回,过程中失败依次将前一节点的waitStatus该为-1且线程park后会阻塞,待unpark后继续循环,直到抢占成功,如遇到异常情况,后续执行cancelAcquire
- cancelAcquire:尝试将当前任务移除队列,通过更改prev的next
AQS的release方法,unlock的时候调用
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
- tryRelease:尝试释放锁,如果是可重入锁,根据重入次数依次递减,最后一次释放成功
- unparkSuccessor:lock时park的线程,在上一锁释放后,unpark唤醒队列中第一个任务(head后的第一个node,head可能为虚拟的占位头结点,未保存任何有关任务的数据,也可能为正在执行的任务)。