AQS

71 阅读3分钟

是什么

AQS(全称AbstractQueuedSynchronizer,抽象同步队列),是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石。通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态。

image.png CLH:Craig、Landin and Hagersten 队列,是一个单向链表,AQS中的队列是CLH变体的虚拟双向队列FIFO

AQS为什么是JUC内容中最重要的基石?

  • 和AQS有关的 image.png
  • 进一步理解锁和同步器的关系
    锁,面向锁的使用者,定义了程序员和锁交互的使用层API,隐藏了实现细节,调用即可。同步器,面向锁的实现者,比如Java并发大神DougLee,提出统一规范并简化了锁的实现,屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制等。

能干嘛

加锁会导致阻塞,有阻塞就需要排队,实现排队必然需要队列。 抢到资源的线程直接使用处理业务,抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待(类似银行业务办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等候),但等候线程仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)。既然说到了排队等候机制,那么就一定会有某种队列形成,这样的队列是什么数据结构呢?如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的结点(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的效果。

image.png

深入理解AQS

image.png 有阻塞就需要排队,实现排队必然需要队列。AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对State值的修改。

AQS内部体系架构 image.png

image.png

AQS类内部

  • state变量表示AQS的同步状态
/**
 * The synchronization state.
 */
private volatile int state;
举例说明:比如银行办理业务的受理窗口状态,零就是没人,自由状态可以办理。
大于等于1,有人占用窗口,等着去。
  • AQS的CLH队列
举例说明:银行候客区的等待顾客

image.png

  • 总结
1)有阻塞就需要排队,实现排队必然需要队列
2)state变量+CLH双端队列

AQS内部类Node

  • Node的int变量
//Node的等待状态waitState成员变量
volatile int waitStatus;
举例说明:等候区其它顾客(其它线程)的等待状态,
队列中每个排队的个体就是一个 Node。
  • Node类

无标题.png

image.png

AQS同步队列的基本结构

image.png CLH:Craig、Landin and Hagersten 队列,是个单向链表,AQS中的队列是CLH变体的虚拟双向队列(FIFO)