AQS框架-谈谈对AQS框架的理解

AQS是一个框架,基于它我们可以实现锁和同步器,J.U.C.包中和很多锁和同步器都是基于AQS实现的。

使用AQS的方式通常不是让锁或同步器直接继承AQS类,而是将AQS的子类作为锁或同步器类的一个辅助内部类,锁或同步器的方法调用AQS子类对象的方法完成同步操作。


(图片来自网络)

state字段

AQS中最重要的一个字段就是同步状态字段state,锁和同步器的实现都是围绕着这个字段的修改展开的,AQS中也暴露出了一些方法供我们重写以操作这个字段,例如tryAcquire, tryRelease, tryAcquireShared, tryReleaseShared,这四个方法底层又是调用AQS提供的getState, setState, compareAndSwapState来修改同步状态。

AQS可以实现各种不同的锁和同步器的原因之一就是,不同的锁或同步器按照自己的需要可以对同步状态的含义有不同的定义,并重写对应的tryAcquire, tryRelease或tryAcquireshared, tryReleaseShared等方法来操作同步状态。

  • 例如,ReentrantLock中,同步状态state代表独占锁的重入次数,获取锁时+1,释放锁时-1。
  • 而在ReentrantReadWriteLock中,同步状态state被划分为高16位和低16位,分别表示读锁计数和写锁计数。

基于队列的线程管理

对于修改同步状态失败的线程,以及调用await而阻塞的线程,AQS替我们提供了这些线程的管理机制,包括线程的排队、阻塞和唤醒等等。这些线程的管理机制对于锁和同步器的编写者来说透明的,他们也没有必要修改这些线程管理相关的方法,因此这些方法在AQS类中被声明为final的,这里用到了模板方法设计模式。

AQS内部的线程管理是基于队列的,AQS内部有两个队列可以用来管理线程,分别是同步队列条件队列

同步队列是基于CLH队列改进得到的,它是一个基于双链表的队列,获取同步状态失败而需要阻塞的线程会被放入同步队列,并在合适的时机被唤醒。同步队列中的节点有独占模式共享模式,对于独占模式,同一时刻只能有一个线程持有锁;对于共享模式,同一时刻可以有多个线程持有锁,针对这两个模式的节点,AQS分别提供了不同的API。

条件队列是一个基于单链表的队列,它主要是为了实现await和signal方法,这两个方法类似Object类中的wait和signal。当线程调用await方法时,它会被封装为Node节点放入条件队列末尾,释放同步状态然后阻塞;当await阻塞的线程被唤醒或中断时,会导致转移到同步队列,然后恢复同步状态。

队列访问方法

此外,AQS暴露出来了访问同步队列和条件队列状态的方法,如下图所示:

通过这些方法我们可以访问同步队列的长度、获取同步队列中所有独占模式线程/共享模式线程,可以查询当前线程对应的节点在同步队列中是否有前驱 (hasQueuedProcessors())等等。

借助这些方法,我们可以实现一些额外的特性。例如借助hasQueuedProcessors()方法我们可以实现公平性ReentrantLock中的公平锁就用到了这点,每次尝试修改同步状态前,必须先调用该方法确保同步队列中没有在该线程之前就开始等待的线程之后,才能够修改同步状态;否则,该节点需要排队等待。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值