AbstractQueuedSynchronizer(AQS)的CLH队列在遇到中断或超时时,是如何精确处理节点状态并保证线程安全的?
2条回答 默认 最新
檀越@新空间 2025-11-27 02:05关注上午好☀️☀️☀️️
本答案参考通义千问AbstractQueuedSynchronizer(AQS)的CLH队列在处理线程中断或超时的情况下,通过节点状态的精确管理和原子操作来保证线程安全。下面将详细说明其机制,并以有序列表的方式列出关键点。
一、AQS CLH队列的基本结构
- AQS 使用 双向链表 维护等待获取锁的线程,这个链表被称为 CLH队列(Craig, Landin, and Hagersten queue)。
- 每个节点(Node)包含以下关键字段:
waitStatus:表示当前节点的状态(如CANCELLED、SIGNAL、CONDITION、PROPAGATE等)thread:绑定的线程prev和next:指向前驱和后继节点
二、处理中断或超时的关键机制
当一个线程在等待锁的过程中被中断(interrupted)或超时(timeout),AQS 会根据这些事件对节点状态进行修改,并确保线程安全。
1. 中断处理
- 线程在调用
acquire()或acquireShared()等方法时,如果被中断,则会抛出InterruptedException。 - AQS 会检查线程是否被中断,并在适当的时候取消当前节点,并唤醒后续节点。
关键代码示例(来自
AbstractQueuedSynchronizer):public final void acquire(int arg) throws InterruptedException { if (Thread.interrupted()) { throw new InterruptedException(); } if (tryAcquire(arg)) { return; } // ... 其他逻辑 }注意: 在
acquire方法中,首先检查线程是否被中断,如果是则抛出异常,避免继续阻塞。2. 超时处理
- 当调用
tryAcquireNanos(long nanosTimeout)时,AQS 会在指定时间内尝试获取锁。 - 如果超时未获取到锁,线程会被取消,并从队列中移除。
关键代码示例(来自
AbstractQueuedSynchronizer):public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; if (Thread.interrupted()) throw new InterruptedException(); // ... 尝试获取锁 }注意: 超时处理也会优先检查线程是否被中断。
三、节点状态的精确管理
AQS 的 CLH 队列通过**节点状态(waitStatus)**来控制线程的等待行为。
| 状态值 | 含义 | |--------|------| | CANCELLED | 节点被取消,不再参与同步 | | SIGNAL | 后继节点需要被唤醒 | | CONDITION | 节点在条件队列中 | | PROPAGATE | 用于共享模式下传播释放信号 |
3. 取消节点(Cancel Node)
当线程被中断或超时时,AQS 会将当前节点标记为 CANCELLED,并唤醒后继节点。
关键方法:
private void cancelAcquire(Node node) { if (node == null) return; node.thread = null; // 清除线程引用 Node pred = node.prev; while (pred.waitStatus > 0) { pred = pred.prev; // 找到前驱非取消节点 } node.prev = pred; if (pred.next == node) { pred.next = node.next; // 移除当前节点 } // 唤醒后继节点 if (node.waitStatus == Node.CONDITION) { LockSupport.unpark(node.thread); // 唤醒线程 } }重点: 取消节点时,会清除线程引用,并更新前驱节点的 next 指针,确保队列正确性。
4. 唤醒后继节点
- 当某个节点被取消后,AQS 会尝试唤醒其后继节点,以确保队列中没有“死锁”或无效节点。
- 这是通过
LockSupport.unpark(thread)实现的。
四、保证线程安全的机制
-
CAS(Compare and Swap)操作
- AQS 中大量使用 CAS 操作来更新节点状态和队列链接。
- 例如:
compareAndSetTail、compareAndSetHead等。
-
volatile 字段
head、tail、waitStatus等字段都使用volatile修饰,确保可见性和有序性。
-
原子操作与锁机制
- AQS 本身不提供锁,而是由子类实现
tryAcquire()、tryRelease()等方法,这些方法通常使用 CAS 来保证原子性。
- AQS 本身不提供锁,而是由子类实现
五、总结:AQS CLH 队列如何处理中断或超时?
- 检查中断状态:在等待前检查线程是否被中断,若被中断则抛出异常。
- 取消节点:当线程被中断或超时时,将节点标记为 CANCELLED。
- 更新队列:移除被取消的节点,并更新前驱和后继节点的链接。
- 唤醒后继节点:通过
LockSupport.unpark()唤醒下一个等待的线程。 - 使用 CAS 和 volatile 确保线程安全:所有操作均基于原子指令和内存屏障。
六、示例代码:自定义 AQS 实现中断处理
public class MyAQS extends AbstractQueuedSynchronizer { protected boolean tryAcquire(int arg) { if (arg == 1) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } } return false; } protected boolean tryRelease(int arg) { if (arg == 1) { setExclusiveOwnerThread(null); setState(0); return true; } return false; } public void lockInterruptibly() throws InterruptedException { if (Thread.interrupted()) { throw new InterruptedException(); } acquire(1); } }说明: 在
lockInterruptibly()方法中,首先检查线程是否被中断,若被中断则抛出异常,避免线程无限等待。
如果你有具体的应用场景或想了解其他机制(如共享模式下的处理),可以继续提问!
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报