Java AQS(Abstract Queued Synchronized)深度解析
一、AQS概述
AQS是Java并发包中的核心框架,为构建锁和同步器提供了基础实现。它是JUC(java.util.concurrent)包中大多数同步类的基石,如ReentrantLock、Semaphore、CountDownLatch等都基于AQS实现。
1.1 AQS核心思想
AQS的核心思想是:
- 如果请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态
- 如果共享资源被占用,则需要一套线程阻塞等待以及被唤醒时锁分配的机制
AQS使用一个volatile的int成员变量state
来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,通过CAS完成对State值的修改。
1.2 AQS主要功能
AQS主要完成三个核心任务:
- 同步状态(如计数器)的原子性管理
- 线程的阻塞和唤醒
- 等待队列的管理
二、AQS核心组件
2.1 同步状态(state)
AQS使用一个volatile int变量state
来表示同步状态:
private volatile int state;
state的具体含义由实现类决定:
- 在ReentrantLock中,表示锁的占有情况(0表示未被占用,>0表示重入次数)
- 在Semaphore中,表示剩余许可证数量
- 在CountDownLatch中,表示还需要倒数的数量
AQS提供了三种操作state的方法:
protected final int getState() {return state;
}protected final void setState(int newState) {state = newState;
}protected final boolean compareAndSetState(int expect, int update) {return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2.2 同步队列(CLH队列)
AQS维护一个FIFO的双向链表(CLH队列变体),用于存放等待获取锁的线程
。队列中的节点是Node类的实例,主要包含以下属性:
static final class Node {volatile int waitStatus; // 节点状态volatile Node prev; // 前驱节点volatile Node next; // 后继节点volatile Thread thread; // 节点关联的线程Node nextWaiter; // 条件队列的下一个节点
}
节点状态waitStatus有四种取值:
- CANCELLED(1):节点被取消
- SIGNAL(-1):后继节点需要被唤醒
- CONDITION(-2):节点在条件队列中等待
- PROPAGATE(-3):共享模式下状态需要传播
2.3 条件队列
AQS还支持条件队列(ConditionObject),用于实现等待/通知机制。与同步队列不同,条件队列是单向链表。
条件队列的使用方式:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();// 等待条件
lock.lock();
try {condition.await();
} finally {lock.unlock();
}// 通知条件
lock.lock();
try {condition.signal();
} finally {lock.unlock();
}
三、AQS工作原理
3.1 独占模式(如ReentrantLock)
3.1.1 获取锁流程
- tryAcquire:子类实现,尝试获取锁
- addWaiter:获取失败,将当前线程包装成Node加入同步队列尾部
- acquireQueued:在队列中自旋尝试获取锁,失败则阻塞
关键代码:
public final void acquire(int arg) {if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}
3.1.2 释放锁流程
- tryRelease:子类实现,尝试释放锁
- unparkSuccessor:唤醒后继节点
关键代码:
public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;
}
3.2 共享模式(如CountDownLatch)
3.2.1 获取共享锁
public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0)doAcquireShared(arg);
}
3.2.2 释放共享锁
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;
}
四、AQS在JUC中的应用
4.1 ReentrantLock
ReentrantLock通过内部类Sync(继承AQS)实现锁功能,分为公平锁和非公平锁两种实现。
公平锁与非公平锁区别:
- 非公平锁在lock()时直接尝试CAS获取锁,不检查队列
- 公平锁在tryAcquire()中会检查是否有前驱节点在等待
4.2 CountDownLatch
CountDownLatch使用AQS的共享模式实现。初始化时设置state为计数次数,countDown()减少state,await()等待state变为0。
4.3 Semaphore
Semaphore也是共享模式实现,state表示可用许可证数量。acquire()获取许可证,release()释放许可证。
五、AQS设计模式
AQS采用模板方法模式,定义了获取/释放锁的骨架逻辑,具体实现由子类完成。
需要子类实现的方法:
- tryAcquire/tryRelease:独占模式
- tryAcquireShared/tryReleaseShared:共享模式
- isHeldExclusively:是否独占
六、AQS性能优化
- CAS操作:通过Unsafe类实现原子状态更新
- 自旋等待:线程不会立即阻塞,而是先自旋尝试获取锁
- 队列优化:减少线程上下文切换
七、AQS与synchronized对比
特性 | AQS | synchronized |
---|---|---|
实现方式 | Java代码实现 | JVM内置实现 |
锁获取方式 | 可中断、可超时 | 不可中断 |
公平性 | 可支持公平/非公平 | 非公平 |
条件队列 | 支持多个条件 | 单个条件 |
性能 | 高竞争下表现更好 | 低竞争下更优 |
八、AQS最佳实践
- 优先使用JUC提供的同步工具类
- 需要自定义锁时再继承AQS
- 合理选择独占/共享模式
- 注意锁的公平性选择
AQS是Java并发编程的核心框架,理解其原理对于掌握Java并发编程至关重要。通过AQS,开发者可以构建各种复杂的同步器,满足不同的并发控制需求。