定义
AQS
AQS(Abstract Queued Synchronizer,抽象队列同步器)是一套多线程同步器框架。
数据结构
exclusiveOwnerThread
记录当前独占锁的拥有线程的引用。
state
private volatile int state;不同同步器对state赋予不同含义,并通过getState()、setState()、compareAndSetState()对其进行操作。
- ReentrantLock:重入次数。
- Semaphore:可用许可数。
- CountDownLatch:剩余计数,为0时唤醒等待线程。
- ReentrantReadWriteLock:高16位读锁,低16位写锁。
CLH变体双向队列
static final class Node {
static final Node SHARED = new Node(); // 共享模式标记
static final Node EXCLUSIVE = null; // 独占模式标记
volatile int waitStatus; // 节点状态
volatile Node prev;
volatile Node next;
volatile Thread thread; // 线程
Node nextWaiter; // 条件队列或共享模式标记
}队列的头部为一个虚拟节点,未抢到锁的线程会被包装成一个Node节点通过CAS尾插法进入队列。
waitStatus状态:
| 值 | 名称 | 含义 |
|---|---|---|
| 0 | 新节点默认值 | |
| -1 | SIGNAL | 后继节点处于等待状态。当前节点在释放锁或取消时,需要负责唤醒后继节点 |
| 1 | CANCELLED | 线程因超时或中断被取消,会被移出队列 |
| -2 | CONDITION | 节点在条件队列中等待 |
| -3 | PROPAGATE | 仅在共享模式下有效,表示后续的获取状态需要向后传播 |
资源共享模式
独占模式(Exclusive):只有一个线程能获取锁(如 ReentrantLock)。
共享模式(Shared):多个线程可同时获取(如 Semaphore、CountDownLatch、读写锁的读锁)。
核心操作
acquire
acquire是AQS用于独占式获取锁的方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}tryAcquire由子类实现:ReentrantLock:state为0时CAS抢锁,state大于0时若为同一线程则重入。
addWaiter(Node.EXCLUSIVE):
private Node addWaiter(Node mode) { // mode = Node.EXCLUSIVE
Node node = new Node(Thread.currentThread(), mode); // 将当前线程包装为一个独占Node
Node pred = tail; // 尾节点,即前驱
if (pred != null) { // 已经初始化
node.prev = pred; // 设置前驱
if (compareAndSetTail(pred, node)) { // CAS设置tail
pred.next = node; // 设置前驱的后继
return node;
}
}
enq(node); // 初始化
return node;
}acquireQueued(final Node node, int arg):
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { // 开始自旋
final Node p = node.predecessor(); // 前驱
if (p == head && tryAcquire(arg)) { // 前驱是头结点才进行抢锁
setHead(node); // 将当前节点变为头结点
p.next = null; // 断开p的连接,使其在下一次GC时被回收
failed = false;
return interrupted;
}
// 拿不到锁,shouldParkAfterFailedAcquire检查前驱状态:
// -1:true,进入挂起
// > 0:false,向前跳过所有取消的节点
// <= 0 且非 -1:通过CAS将前驱状态改为-1
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true; // park期间被中断
}
} finally {
if (failed)
cancelAcquire(node);
}
}selfInterrupt:拿到锁后补发中断。
release
release是AQS用于独占式释放锁的方法。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}tryRelease(arg):子类尝试释放锁。ReentrantLock:state-1,直到0时完全释放锁。
unparkSuccessor(Node h):- 将头节点的
waitStatus设为0。 - 找到头结点后的第一个有效后继节点s。当s为null或
s.waitStatus==1时,通过tail从尾往前找离head最近的节点(先CAS设置tail后才设置尾节点的后继,因此从tail保证正确查找)。 - 调用
LockSupport.unpark(s.thread)唤醒后继线程。
- 将头节点的
公平锁和非公平锁
lock():- 非公平锁:先CAS一次,失败再acquire(1)。
- 公平锁:直接acquire(1)。
tryAcquire():- 公平锁:在c为0时,先调用
!hasQueuedPredecessors()检查是否有已经在排队的节点。
- 公平锁:在c为0时,先调用
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}性能:非公平锁的性能通常远高于公平锁,因为上一个线程唤醒下一个线程并恢复运行存在可能的上下文切换和内核态开销,需要一定的时间,此时非公平锁其他线程可以直接抢到锁并进行业务,无需等待。因此ReentrantLock默认实现为非公平锁。