Appearance
AQS LockSupport源码分析
一、LockSupport概述
LockSupport是Java并发包中用于构建锁和其他同步组件的基础工具类,通过park()
和unpark()
方法实现线程的阻塞与唤醒。它类似于信号量机制,但其"许可证"最多只有一个。
二、核心方法解析
1. park() 方法
java
public static void park() {
UNSAFE.park(false, 0L);
}
- 功能:阻塞当前线程,直到其他线程调用
unpark()
或线程被中断。 - 许可证机制:如果线程已有许可证(即之前调用过
unpark()
),则立即返回并消费该许可证;否则线程进入阻塞状态。 - 中断处理:若线程在阻塞期间被中断,会立即返回且不抛出异常,需通过
Thread.interrupted()
检测中断状态。
2. unpark(Thread thread) 方法
java
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
- 功能:为指定线程发放许可证,使其从
park()
中返回。 - 特点:
- 可以先于
park()
调用,确保线程不会阻塞。 - 多次调用
unpark()
仅发放一个许可证。
- 可以先于
三、底层原理简析
每个线程内部维护一个Parker
对象,包含三个关键部分:
_counter
:表示是否可以调用park()
的许可证计数(最大为1)。_cond
:条件变量,用于线程阻塞等待。_mutex
:互斥锁,保证线程安全地访问_counter
。
1. park() 的执行流程
- 检查
_counter
值:- 若为1,则直接消费许可证并继续执行,无需阻塞。
- 若为0,则获取
_mutex
锁,并将线程加入_cond
条件队列等待。
- 线程进入阻塞状态,等待
unpark()
唤醒。
2. unpark(Thread thread) 的执行流程
- 设置目标线程的
_counter
为1。 - 若目标线程已处于阻塞状态,则唤醒它。
- 目标线程恢复后,检查
_counter
并消费许可证。
四、使用示例
以下是一个基于LockSupport
实现的简单FIFO互斥锁示例:
java
class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<>();
public void lock() {
boolean wasInterrupted = false;
waiters.add(Thread.currentThread());
while (waiters.peek() != Thread.currentThread() || !locked.compareAndSet(false, true)) {
LockSupport.park(this);
if (Thread.interrupted())
wasInterrupted = true;
}
waiters.remove();
if (wasInterrupted)
Thread.currentThread().interrupt();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
- 说明:
lock()
方法中,线程必须是队首元素才能尝试获取锁。- 若锁已被占用,则线程调用
park()
进入阻塞状态。 unlock()
方法释放锁后,唤醒队首线程继续竞争锁。
五、总结
- 优势:
- 支持精确唤醒特定线程。
- 不依赖对象监视器(monitor),使用更灵活。
- 允许先调用
unpark()
再调用park()
。
- 应用场景:
- 构建高级同步工具(如AQS框架中的各类锁)。
- 需要精确控制线程阻塞与唤醒的场景。
通过上述分析可见,LockSupport
是Java并发编程中非常强大的基础工具,尤其在AQS框架中扮演着重要角色。