一文轻松看透AQS的核心设计思想

一、AQS是什么

AQS的完整类名是——AbstractQueuedSynchronizer(直译过来就是抽象队列同步器)。从字面上就能看出来,是专门用于实现同步功能的一个抽象框架。事实上,AQS是著名的JDK并发包设计者Doug Lea定义的一套用于控制多线程并发访问共享资源的基础框架,许多并发包工具实现都依赖于它,如常用的ReentrantLock、ReadWriteLock、Semaphore、CountDownLatch和ThreadPoolExecutor 的 Worker

二、为什么要使用AQS

设想一下,如果没有AQS,要实现一个锁需要如何进行?

如果没有 AQS,那么就需要开发者自己去实现以下内容,至少包括:

  • 同步状态的原子性管理(比如锁状态的获取和释放)
  • 线程的阻塞与唤醒
  • 阻塞队列的管理

举个例子:实现一个类似ReentrantLock功能的可重入锁 ,需要一个变量维护锁被重入的次数,同时必须保证多线程同时操作该变量时是线程安全的;同时竞争锁失败的线程,可能需要让它们陷入阻塞并进行排队,同时要在合适的时机将其唤醒。
其实不只是对于锁而言,其他的进行多线程并发控制的同步组件基本都需要上述这些操作。这一系列的操作流程都是繁杂而重复的,此时AQS的意义就凸显出来了。

AQS定义好了这一套骨架流程的操作,不同的同步组件只需要继承AQS并重写指定的方法,就可以实现同步组件自身的
功能逻辑。这其实就是模板方法的思想

总结一下,就是:同步组件是面向普通开发者的,AQS是面向同步组件开发者的

三、AQS 的设计原理

AQS的实现源码有2000多行代码,相对还是比较复杂的。但是我们将其实现细节抛开,其实重点就围绕着以下三大部分:

  • state 状态
  • CLH队列
  • 获取/释放

3.1 state 状态

如果我们的 AQS 想要去管理或者想作为协作工具类的一个基础框架,那么它必然要管理一些状态,而这个状态在 AQS 内部就是用 state 变量去表示的。它的定义如下:

    /**
     * The synchronization state.
     */
    private volatile int state;

而 state可以根据具体组件类的作用不同而表示不同的含义,比如:

  • 信号量Semaphore:state 表示剩余许可的数量。比如我们把 state 初始化设置为 5,表示许可证初始一共有5个,然后当某个线程取走1个许可证之后,这个 state 就会变为4,所以信号量的 state 相当于是一个内部计数器。
  • CountDownLatch :state 表示是需要“倒数”的数量。一开始我们假设把它设置为 5,当每次调用 CountDown 方法时,state 就会减 1,一直减到 0 的时候就代表这个门闩被放开。
  • ReentrantLock :表示的是锁的占有情况。初始化时是 0,表示没有线程占有锁;如果 state 变成了 1,则代表这个锁已经被某个线程所持有了。
  • ReadWriteLock:高16位表示读锁状态,低16位表示写锁状态。

下面看AQS提供的state状态的获取和变更操作:

  /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

1.getState读取——由于state变量带有volatile关键字进行修饰,能保证内存可见性,getState方法获取就能直接最新的值。
2.setState直接写入——直接对state = newState;赋值操作,此操作一般是用于同步组件在独占之后的状态变更设置,无进行额外同步。
3.compareAndSetState(int expect, int update) CAS操作写入,借助Unsafe工具类的 CAS 操作,利用 CPU 指令的原子性保证了这个操作的原子性。

3.2 CLH队列 (FIFO)

CLH队列作用是存储竞争失败从而进入阻塞等待状态的线程。
队列内部是双向链表的结构,分别用 head 和 tail 来表示头尾节点, 初始化的时候两个节点都指向了一个空节点。
头节点当做当前持有锁的线程,尾节点则是每个新的阻塞线程进来队列时要插入的位置,插入尾节点需要通过CAS进行插入操作原子性控制。
在这里插入图片描述
(图文引用自AQS英文文档

3.3 同步状态的获取和释放

在这里插入图片描述

AQS定义了独占式获取与释放同步状态和共享式获取与释放同步状态4中操作类型,需要子类自己实现。

四、小结

本文介绍了什么AQS框架出现的背景作用以及AQS核心的设计原理。 AQS是面向同步组件开发者的基础框架,使用模板方法设计模式的思想,将同步状态的管理 、 阻塞队列和线程的阻塞唤醒等处理流程定义成固定的模板。组件开发者无需关注底层细节,只需要重写少数几个相关方法即可。

参考资料:AQS官方文档

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeMavs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值