虚拟队列同步器(Abstract Queued Synchronizer, AQS)的工作机制可以概述如下:当一个线程尝试获取某个“锁”资源时,它首先会检查与该锁关联的状态变量(state)。如果当前状态表明此锁是可以被立即获取的,则该线程将成功获取锁并继续执行。反之,若状态指示锁已被其他线程持有或出于某种原因不可用,请求中的线程不会立即获得锁。此时,AQS会将这个线程封装成一个节点,并将其添加到一个基于FIFO原则管理的等待队列中。在这个队列里,线程按照它们请求锁的时间顺序排队,直到轮到它们或者满足特定条件时才能尝试重新获取锁。这样的设计确保了即使在高并发场景下也能维持良好的公平性和效率。
AQS的核心功能就在如何去判断一个线程能否获取锁,并且如何安全高效管理这个先进先出的队列。
为了理解这个核心过程,笔者这篇文章来分析下AQS获取锁的核心方法 acquire();这个方法在jdk22中加上注释只有90行,却让笔者直呼巧妙,让我们一起来分析一下源码。
在讲解acquire之前,先来回顾一下AQS维护的CLH变体队列的结构
AQS中包含若干node节点,而一个node的基本结构如下,一个指向前继的指针 prev,一个后继指针next,代表线程的waiter,以及最后表示节点状态的status。
abstract static class Node {
volatile Node prev; // initially attached via casTail
volatile Node next; // visibly nonnull when signallable
Thread waiter; // visibly nonnull when enqueued
volatile int status; // written by owner, atomic bit ops by others
}
整体的队列结构如图所示,需要注意的是HEAD指向的节点是一个虚节点,并不表示正在排队的线程,HEAD指向的next节点才是队列中第一个节点。另外HEAD和TAIL两个变量在采用懒加载模式,当第一次发生节点需要入队的时候,才需要初始化,初始化后HEAD == TAIL,未加载之前值为NULL。

接下来是AQS的核心acquire方法,搞懂acquire方法已经可以相当于搞懂了百分之80的AQS。
话不多说直接放代码,如果在对AQS没有基本了解的情况下去读源码避免不了一头雾水。笔者已经将代码分为了不同的部分,来一块块讲解每块代码在干什么,有什么作用。
final int acquire(Node node, int arg, boolean shared,
boolean interruptible, boolean timed, long time) {
//part0
Thread current = Thread.currentThread();
byte spins = 0, postSpins = 0; // retries upon unpark of first thread
boolean interrupted = false, first = false;
Node pred = null; // predecessor of node when enqueued
/*
* Repeatedly:
* Check if node now first
* if so, ensure head stable, else ensure valid predecessor
* if node is first or not yet enqueued, try acquiring
* else if node not yet created, create it
* else if not yet enqueued, try once to enqueue
* else if woken from park, retry (up to postSpins times)
* else if WAITING status not set, set and retry
* else park and clear WAITING status, and check cancellation
*/
// 循环开始
for (;;) {
//part1,
if (!first && (pred = (node == null) ? null : node.prev) != null &&
!(first = (head == pred))) {
if (pred.status < 0) {
cleanQueue(); // predecessor cancelled
continue;
} else if (pred.prev == null) {
Thread.onSpinWait(); // ensure serialization
continue;
}
}
//part2
if (first || pred == null) {
boolean acquired;
try {

1034

被折叠的 条评论
为什么被折叠?



