回答
AQS
是 AbstractQueuedSynchronizer 简称,它是 J.U.C 包中多个组件的底座,如 ReentrantLock
、CountDownLatch
、Semaphore
、CyclicBarrier
都是基于 AQS 来实现的。
AQS
底层利用 volatile 类型的 int state 来表示同步状态,该字段的含义取决于实现的同步器,如在ReentrantLock
中,state 表示持有锁的数量,在 Semaphore
中,state 表示可用的许可证数量。当线程来获取同步锁时,如果 status = 0,说明目前没有任何线程占有锁,该线程可以获得锁并设置 state = 1。如果 state > 0,则说明有线程正在持有锁,则线程必须加入同步队列进行等待(不考虑重入)。
AQS
内部还维护着一个 FIFO 的双向同步队列,该队列通过 Node 的实例来构建的,每个 Node 代表一个等待获取资源的线程。获取锁失败的线程都会被包装成一个 Node 节点加入队列中并进入阻塞状态。
AQS 有两种锁机制:排他锁和共享锁。
- 排他锁:同一个时间只能有一个线程访问该共享资源,为独占。
- 共享锁:允许多个线程同时访问共享资源,一般来说它是读锁。
详解
AQS 设计
先看 AQS 有哪些重要属性:
// 头结点
private transient volatile Node head;
// 尾节点
private transient volatile Node tail;
// AQS 中最重要的变量。它表示当前锁资源的状态
// = 0 时,表示当前锁没有被占用
// > 0 时,表示有线程持有锁
// state 可以大于 1,因为锁支持可重入
private volatile int state;
// 当前持有所有的线程
// 这个变量位于 AbstractOwnableSynchronizer 中,为了更好地理解,大明哥将其放到这里来了
private transient Thread exclusiveOwnerThread;
AQS 内部有一个 FIFO 的同步队列,即 CLH 同步队列,如果某个线程获取锁失败,则会被包装为一个 Node 节点加入到 CLH 同步队列中,同时被阻塞。其结构如下:
head
:CLH 队列的头结点,我们可以认为它就是持有锁的当前线程的节点。而且,head 节点是延迟初始化的,只有它在首次使用时才会被创建。有两点需要注意:- 阻塞队列不包含 head 节点
- 如果 head 节点存在,那么它的
waitStatus
一定不会是CANCELLED
。这说明 head 不会表示一个已取消其等待状态的线程。
tail
:CLH 队列的尾节点,当某个线程获取锁失败时,会被加入到 CLH 队列的尾部,同时 tail 会指向这个节点。
每一个等待的线程都会被包装成一个 Node 实例:
static final class Node {
/** 标识节点当前处于共享模式下 */
static final Node SHARED = new Node();
/** 标识节点当前处于独占模式下 */
static final Node EXCLUSIVE = null;
/** 表示此线程因为超时或中断而不再需要争夺这个锁了 */
static final int CANCELLED = 1;
/** 表示当前节点的线程释放或取消时,应该通知后继节点的线程 */
static final int SIGNAL = -1;
/** 表示节点在条件队列中,与条件变量相关联 */
static final int CONDITION = -2;
/** 表示下一个 acquireShared 应该无条件地传播 */
static final int PROPAGATE = -3;
/* 节点状态,有上面四个值(1、-1、-2、-3) */
volatile int waitStatus;
/* 前驱节点 */
volatile Node prev;
/* 后继节点*/
volatile Node next;
/* 包装的线程*/
volatile Thread thread;
/* 这个字段用于链接下一个等待在条件上的节点 ,与 next 意义一致,但是它用于条件队列*/
Node nextWaiter;
}
下图是 AQS 的模型图:
AQS 底层采用模板方法模式,子类需要实现对共享成员变量 state 的获取和释放,即获取锁和释放锁。至于 CLH 队列的维护、线程的阻塞和唤醒由 AQS 实现。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。