在Java并发编程中,AbstractQueuedSynchronizer
(简称AQS)是构建锁
和同步器
的核心框架。它通过提供状态管理
、线程排队
和阻塞唤醒
机制,为开发者构建高效的线程协作工具
提供了底层支持。
AQS的内部结构由三个核心部分构成:状态(state)、FIFO队列以及获取/释放方法。这三部分共同协作,实现了线程的高效调度与资源管理。
AQS通过一个volatile int state
变量来表示同步器的状态。state
的含义由具体的子类决定,例如:
state
表示锁的重入次数(0表示未被占用)。state
表示剩余许可证的数量。state
表示需要倒数的次数(减至0时触发事件)。由于state
会被多个线程并发修改,AQS通过CAS(Compare and Swap)
操作和volatile
关键字确保其线程安全性:
compareAndSetState(int expect, int update)
方法,使用Unsafe
类的原子指令更新state
,保证操作的原子性。setState(int newState)
),volatile
关键字确保了多线程间的可见性。protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
protected final void setState(int newState) {
state = newState;
}
AQS维护了一个双向链表结构的等待队列,用于管理未获得资源的线程
。该队列遵循FIFO(先进先出)原则,确保线程公平竞争资源。
当线程无法获取资源时,会被封装为Node
对象插入队列尾部,并进入阻塞状态。释放资源时,AQS会唤醒队列中的下一个线程(即头节点的下一个节点),使其尝试获取资源。
AQS通过LockSupport.park()
和LockSupport.unpark()
实现线程的阻塞与唤醒:
park()
,进入阻塞状态。unpark()
唤醒队列中的下一个线程。这种机制避免了线程的“忙等”,有效减少CPU资源消耗
。
AQS定义了抽象的获取(acquire)和释放(release)方法,由具体的子类(如ReentrantLock、Semaphore)实现业务逻辑。这些方法的核心逻辑依赖于state
的值和队列的管理。
获取方法的流程如下:
state
:根据state
的值判断是否允许当前线程获取资源。Node
插入队列,并调用park()
阻塞。以ReentrantLock的lock()
方法为例:
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
释放方法的流程如下:
state
:根据业务逻辑调整state
(如释放锁、归还许可证)。unpark()
唤醒下一个等待线程。以ReentrantLock的unlock()
方法为例:
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
AQS通过模板方法模式(Template Method Pattern)
将通用的线程调度逻辑抽象出来,子类只需实现特定的tryAcquire
和tryRelease
方法。这种设计降低了代码重复性,提升了开发效率。
AQS将状态管理、队列操作与业务逻辑解耦,开发者只需关注资源的获取与释放规则,而无需处理复杂的线程调度细节。例如,ReentrantLock
和Semaphore
虽然业务逻辑不同,但都复用了AQS的队列和状态管理机制。
AQS支持非公平锁(默认)和公平锁(通过ReentrantLock(true)
指定)。公平锁通过队列顺序
保证线程按申请顺序获取资源,而非公平锁允许插队(如当前线程释放锁后立即重入)
,从而减少上下文切换开销。
ReentrantLock基于AQS实现了可重入锁:
tryAcquire
检查state
是否为0或当前线程是否已持有锁。tryRelease
将state
减1,若减至0则唤醒等待线程。protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
returntrue;
}
} elseif (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
thrownew Error("Maximum lock count exceeded");
setState(nextc);
returntrue;
}
returnfalse;
}
Semaphore通过AQS管理许可证数量:
acquire()
方法尝试减少state
,若不足则阻塞。release()
方法增加state
并唤醒等待线程。public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
CAS(Compare and Swap)是一种无锁算法,通过硬件指令(如cmpxchg
)实现原子操作,避免了线程阻塞和上下文切换的开销。相比传统锁,CAS更适合高并发场景,但需处理ABA问题(通过版本号解决)。
AQS通过interrupt()
方法标记线程中断状态,并在acquire
方法中检查中断标志。若线程在等待期间被中断,会抛出InterruptedException
或在释放资源后自行处理。
AQS支持自定义同步器,开发者可通过继承AbstractQueuedSynchronizer
并实现tryAcquire
和tryRelease
方法,构建符合业务需求的同步工具(如数据库连接池、任务调度器)。
AQS是Java并发编程的基石,其核心思想是通过状态管理、FIFO队列和模板方法模式 ,将线程调度的复杂性抽象为通用框架。掌握AQS的原理不仅有助于理解ReentrantLock、Semaphore等工具的实现,还能提升开发者设计高效并发组件的能力。无论是应对技术面试还是优化业务代码,深入学习AQS都是迈向高级Java开发者的必经之路。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有