在 AQS
(AbstractQueuedSynchronizer) 中,这些方法涉及到同步的获取和排队机制,它们实现了类似于锁(Lock)和信号量(Semaphore)的功能。AQS 通过内部维护一个 FIFO 队列和一些节点来管理线程的同步。下面逐个解释这些方法的作用:
AQS 核心方法和源码
1. acquire(int arg)
- 作用:尝试获取同步状态,如果失败,则加入队列并阻塞线程。
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt();
}
}
流程:
- 调用
tryAcquire(arg)
尝试获取锁。 - 如果获取失败,则调用
addWaiter()
将线程加入队列。 - 调用
acquireQueued()
进行排队等待。 - 如果等待过程中被中断,调用
selfInterrupt()
重新设置中断状态。
- 调用
2. tryAcquire(int arg)
- 作用:尝试获取同步状态。这个方法是抽象方法,子类需要实现。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
- 注意:具体的同步工具(如
ReentrantLock
)会重写此方法来定义获取同步状态的逻辑。
3. addWaiter(Node.EXCLUSIVE)
- 作用:将当前线程封装成一个
Node
,并加入等待队列末尾。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
流程:
- 封装当前线程为一个
Node
节点。 - 尝试将节点加入等待队列尾部,如果失败则调用
enq()
进行入队。
- 封装当前线程为一个
4. enq(Node node)
- 作用:将节点安全地加入等待队列末尾(无限重试,直到成功)。
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // 队列未初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
流程:
- 检查队列是否存在头节点,如果不存在,初始化。
- 使用 CAS 操作将新节点加入队列尾部,保证线程安全。
5. acquireQueued(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; // 帮助 GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
interrupted = true;
}
}
} finally {
if (failed) cancelAcquire(node);
}
}
流程:
- 获取前驱节点。
- 如果前驱节点是头节点,则尝试获取同步状态。
- 如果获取失败,调用
shouldParkAfterFailedAcquire()
判断是否需要挂起线程。 - 挂起线程并检查中断状态。
6. release(int arg)
- 作用:释放同步状态,唤醒后继线程。
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)
释放同步状态。 - 如果释放成功,则唤醒等待队列中的后继线程。
- 调用
7. tryRelease(int arg)
- 作用:尝试释放同步状态。这个方法是抽象方法,需要子类实现。
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
- 注意:子类重写此方法,定义释放逻辑。
8. unparkSuccessor(Node node)
- 作用:唤醒等待队列中的下一个线程。
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
流程:
- 将当前节点的
waitStatus
重置。 - 找到下一个需要唤醒的线程节点。
- 使用
LockSupport.unpark()
唤醒线程。
- 将当前节点的
9. shouldParkAfterFailedAcquire()
- 作用:检查节点的状态,决定线程是否需要挂起。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
总结核心方法
方法名 | 作用 |
---|---|
acquire(int arg) | 获取同步状态,失败则排队等待。 |
tryAcquire(int arg) | 尝试获取同步状态,子类实现。 |
addWaiter(Node.EXCLUSIVE) | 添加节点到等待队列末尾。 |
enq(Node node) | 使用 CAS 操作将节点加入队列末尾。 |
acquireQueued() | 线程在队列中等待,直到获取同步状态。 |
release(int arg) | 释放同步状态并唤醒等待线程。 |
tryRelease(int arg) | 尝试释放同步状态,子类实现。 |
unparkSuccessor(Node node) | 唤醒等待队列中的下一个线程。 |
shouldParkAfterFailedAcquire() | 判断线程是否需要挂起。 |
其他核心概念:
- Node 类:代表等待队列中的线程节点。
状态管理:
waitStatus
:节点状态,包括SIGNAL
(等待唤醒)和CANCELLED
等。head
和tail
:管理等待队列的头尾节点。
- CAS 操作:确保节点入队等操作的线程安全。
这些方法共同配合,实现了 AQS 的底层同步控制机制,包括线程的排队、阻塞、唤醒等功能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。