AQS
相比原来的 syncornized, ReentrantLock 解决了防死锁、公平、超时控制、精准唤醒的问题。
- 一个双向链表
- 一个
volatile修饰的保证多线程可见性的同步状态state,代表当前对象锁是否被占用和重入次数 - 线程尝试加锁,加锁失败的入队列,公平锁下保证 FIFO,非公平状态允许插队
AQS 是 JUC 包的基石,它内部维护了一个
volatile int state代表同步状态(通过 CAS 更新),以及一个 FIFO 线程等待队列(CLH 双向链表)。当线程请求共享资源时:
- 调用
tryAcquire()尝试获取资源,成功则直接返回。- 若失败,将当前线程封装为 Node 节点 加入等待队列尾部。
- 入队后再次判断:如果前驱是 head,则再次尝试
tryAcquire()。- 若仍失败,调用
LockSupport.park()挂起线程。- 当资源释放时,唤醒队列中的后继节点,被唤醒的线程回到步骤 3。
公平锁 vs 非公平锁的区别:
- 公平锁:新线程在
tryAcquire()前会检查队列中是否有等待者(hasQueuedPredecessors()),有则放弃,严格按队列顺序。- 非公平锁:新线程直接 CAS 抢锁,不管队列中是否有等待者;被唤醒的线程可能竞争失败,需重新排队。
可重入性
同一个线程可以多次获取同一把锁,而不会被阻塞或死锁。
这个例子中通过递归调用在临界区内多次尝试获得同一把锁,由于 ReentrantLock 支持可重入,这里不会导致死锁,计数器 +1 即可,递归回溯的时候则 -1。
java
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockBasic {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public void recursiveIncrement(int depth) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取锁,深度: " + depth);
if (depth > 0) {
recursiveIncrement(depth - 1);
}
count++;
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁, 深度: " + depth);
}
}
public static void main(String[] args) {
ReentrantLockBasic demo = new ReentrantLockBasic();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
demo.recursiveIncrement(3);
}
}, "Thread-1");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
demo.recursiveIncrement(3);
}
}, "Thread-2");
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
demo.recursiveIncrement(3);
}
}, "Thread-3");
thread1.start();
thread2.start();
thread3.start();
try {
thread1.join();
thread2.join();
thread3.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Final count: " + demo.count);
}
}避免死锁
通过 tryLock 尝试获取锁,在获取不到锁的时候可以做其他事情,避免阻塞。
公平锁
通过 new ReentrantLock(true); 配置公平锁,保证队首节点尝试获取锁的时候不会被新进线程抢占。
其他AQS容器
| 特性 | CountDownLatch | Semaphore | CyclicBarrier |
|---|---|---|---|
| 核心作用 | 线程等待事件(计数归零) | 控制并发数量(许可证) | 线程互相等待(集结点) |
| 可重用性 | 一次性(计数归零后失效) | 可重用(释放许可证后可再次获取) | 可循环使用(计数器自动重置) |
| 线程关系 | 主线程等待多个子线程 | 线程间竞争资源 | 线程间互相等待 |
| 典型场景 | 资源加载、并发测试 | 连接池限流、服务降级 | 并行计算、多阶段任务 |
CAS
比较并交换。