引言
AbstractQueuedSynchronizer(AQS)是 Java 并发包的基石。ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier 等核心同步器都基于 AQS 实现。理解 AQS 的原理,是深入理解 Java 并发编程的关键。
一、AQS 核心原理
1.1 AQS 是什么
AQS 是一个用于构建锁和同步器的框架,核心思想:
如果被请求的共享资源空闲,则当前线程获取资源;如果被占用,则将当前线程加入等待队列,阻塞等待唤醒。
1.2 核心数据结构
AQS 核心组成:
├── state(int) → 同步状态,由子类定义含义
├── exclusiveOwnerThread → 独占模式下的持有线程
└── CLH 等待队列 → 双向链表,FIFO 排队
CLH 队列结构:
┌──────────┐ ┌──────────┐ ┌──────────┐
head │ prev=0 │◄───│ prev │◄───│ prev │ tail
│ next ────┼───►│ next ────┼───►│ next=0 │
│ thread=0 │ │ thread=T2│ │ thread=T3│
│ ws=SIGNAL│ │ ws=SIGNAL│ │ ws=0 │
└──────────┘ └──────────┘ └──────────┘
head:哨兵节点(不存储线程)
后续节点:等待获取资源的线程
ws(waitStatus):SIGNAL=-1(需要被唤醒)
1.3 两种模式
| 模式 | 说明 | 典型实现 |
|---|---|---|
| 独占模式(Exclusive) | 只有一个线程能获取资源 | ReentrantLock |
| 共享模式(Shared) | 多个线程可同时获取资源 | Semaphore, CountDownLatch |
1.4 核心方法
AQS 采用模板方法模式,子类只需实现以下方法:
// 独占模式
protected boolean tryAcquire(int arg); // 尝试获取独占资源
protected boolean tryRelease(int arg); // 尝试释放独占资源
// 共享模式
protected int tryAcquireShared(int arg); // 尝试获取共享资源
protected boolean tryReleaseShared(int arg);// 尝试释放共享资源
// 判断是否独占
protected boolean isHeldExclusively();
1.5 获取资源流程
acquire(1) // 独占获取
│
tryAcquire(1)
├─ 成功 → 返回(获取资源)
└─ 失败 ↓
addWaiter(EXCLUSIVE) // 加入 CLH 队列尾部
│
acquireQueued(node, 1) // 在队列中自旋/阻塞
│
├─ 前驱是 head && tryAcquire 成功 → 获取资源
└─ 否则 → shouldParkAfterFailedAcquire → LockSupport.park → 阻塞
1.6 释放资源流程
release(1) // 独占释放
│
tryRelease(1)
├─ 失败 → 返回 false
└─ 成功 ↓
unparkSuccessor(head) // 唤醒后继节点
│
LockSupport.unpark(succ.thread) // 唤醒线程
二、CountDownLatch
2.1 原理
CountDownLatch 基于 AQS 共享模式
state = 计数器初始值
countDown() → state--(释放共享资源)
await() → state == 0 时通过,否则阻塞
CountDownLatch(3)
│
state=3 → state=2 → state=1 → state=0
↓ ↓ ↓ ↓
countDown countDown countDown await 通过!
2.2 基本用法
// 场景:主线程等待 N 个子任务完成
CountDownLatch latch = new CountDownLatch(3);
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
final int taskId = i;
executor.submit(() -> {
try {
doTask(taskId);
} finally {
latch.countDown(); // 计数 -1
}
});
}
latch.await(); // 阻塞等待计数归零
System.out.println("All tasks completed");
executor.shutdown();
2.3 超时等待
// 带超时的等待
if (latch.await(30, TimeUnit.SECONDS)) {
System.out.println("All tasks completed in time");
} else {
System.out.println("Timeout! Some tasks not completed");
}
2.4 典型应用场景
① 多服务并行初始化
public class AppInit {
private static final CountDownLatch INIT_LATCH = new CountDownLatch(3);
public void init() throws InterruptedException {
new Thread(() -> {
initDB();
INIT_LATCH.countDown();
}).start();
new Thread(() -> {
initCache();
INIT_LATCH.countDown();
}).start();
new Thread(() -> {
initMQ();
INIT_LATCH.countDown();
}).start();
INIT_LATCH.await();
System.out.println("All services initialized");
}
}
② 并行数据加载
public class DataLoader {
public Data loadData() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Data data = new Data();
executor.submit(() -> {
try { data.setUsers(fetchUsers()); }
finally { latch.countDown(); }
});
executor.submit(() -> {
try { data.setOrders(fetchOrders()); }
finally { latch.countDown(); }
});
executor.submit(() -> {
try { data.setProducts(fetchProducts()); }
finally { latch.countDown(); }
});
latch.await(10, SECONDS);
return data;
}
}
2.5 注意事项
| 注意 | 说明 |
|---|---|
| 不可重置 | 计数器归零后不能重用,需新建 |
| countDown 必须在 finally | 防止异常导致计数不归零 |
| 避免死锁 | countDown 和 await 不要在同一个线程 |
三、CyclicBarrier
3.1 原理
CyclicBarrier 基于 ReentrantLock + Condition
parties = 参与线程数
count = 还需要等待的线程数
线程1到达 → await() → count-- → count≠0 → 阻塞
线程2到达 → await() → count-- → count≠0 → 阻塞
线程3到达 → await() → count-- → count=0 → 唤醒所有线程 + 重置
特点:可循环使用(Cyclic)
3.2 基本用法
// 3 个线程互相等待,全部到达后继续
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("All threads reached barrier, merging results");
});
for (int i = 0; i < 3; i++) {
final int taskId = i;
new Thread(() -> {
try {
System.out.println("Task " + taskId + " phase 1");
barrier.await(); // 等待其他线程
System.out.println("Task " + taskId + " phase 2");
barrier.await(); // 再次等待(可循环使用)
System.out.println("Task " + taskId + " phase 3");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
3.3 栅栏动作(Barrier Action)
// 所有线程到达后,先执行 barrierAction,再唤醒所有线程
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
// 合并各线程的中间结果
mergeResults();
});
3.4 典型应用场景
① 多阶段并行计算
// 模拟多轮迭代计算
CyclicBarrier barrier = new CyclicBarrier(4, () -> {
// 每轮结束后汇总
System.out.println("Round completed, checking convergence...");
});
for (int i = 0; i < 4; i++) {
final int workerId = i;
new Thread(() -> {
for (int round = 0; round < 10; round++) {
computePart(workerId, round);
barrier.await(); // 等待其他 worker 完成本轮
}
}).start();
}
② 模拟并发请求
// 模拟 100 个用户同时请求
CyclicBarrier barrier = new CyclicBarrier(100);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
barrier.await(); // 所有线程同时开始
httpClient.get("/api/test");
}).start();
}
3.5 CountDownLatch vs CyclicBarrier
| 对比 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 计数方向 | 递减到 0 | 等待到 N |
| 可重用 | ❌ 一次性 | ✅ 可循环 |
| 等待方 | 主线程等待子线程 | 线程间互相等待 |
| 栅栏动作 | 无 | 支持 barrierAction |
| 异常处理 | countDown 在 finally | BrokenBarrierException |
| 实现原理 | AQS 共享模式 | ReentrantLock + Condition |
四、Semaphore
4.1 原理
Semaphore 基于 AQS 共享模式
state = 许可证数量(permits)
acquire() → state--(获取许可证)
release() → state++(释放许可证)
Semaphore(3):
线程1 acquire → state=2 → 获取成功
线程2 acquire → state=1 → 获取成功
线程3 acquire → state=0 → 获取成功
线程4 acquire → state=0 → 阻塞等待
线程1 release → state=1 → 线程4 被唤醒
4.2 基本用法
// 限制同时访问的线程数
Semaphore semaphore = new Semaphore(5); // 最多 5 个并发
public void access() {
try {
semaphore.acquire(); // 获取许可证
try {
doWork(); // 最多 5 个线程同时执行
} finally {
semaphore.release(); // 释放许可证
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
4.3 公平与非公平
// 非公平(默认):新来的线程可能插队
Semaphore unfair = new Semaphore(5);
// 公平:按等待顺序获取
Semaphore fair = new Semaphore(5, true);
4.4 tryAcquire 超时
// 尝试获取,超时返回 false
if (semaphore.tryAcquire(5, TimeUnit.SECONDS)) {
try {
doWork();
} finally {
semaphore.release();
}
} else {
System.out.println("获取许可证超时,执行降级逻辑");
}
4.5 典型应用场景
① 限流
// 数据库连接池限流
public class DBPoolLimiter {
private final Semaphore semaphore;
public DBPoolLimiter(int maxConcurrent) {
this.semaphore = new Semaphore(maxConcurrent);
}
public Connection getConnection() throws InterruptedException {
semaphore.acquire();
try {
return dataSource.getConnection();
} catch (SQLException e) {
semaphore.release(); // 获取连接失败,释放许可证
throw new RuntimeException(e);
}
}
public void releaseConnection(Connection conn) {
try {
conn.close();
} catch (SQLException ignored) {
} finally {
semaphore.release();
}
}
}
② 资源池
// 对象池
public class ObjectPool<T> {
private final Semaphore semaphore;
private final Queue<T> pool;
public ObjectPool(Supplier<T> supplier, int size) {
this.semaphore = new Semaphore(size);
this.pool = new ConcurrentLinkedQueue<>();
for (int i = 0; i < size; i++) {
pool.add(supplier.get());
}
}
public T borrowObject() throws InterruptedException {
semaphore.acquire();
return pool.poll();
}
public void returnObject(T obj) {
pool.offer(obj);
semaphore.release();
}
}
五、自定义同步器
5.1 不可重入锁
public class NonReentrantLock implements Lock {
private final Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();
}
Condition newCondition() {
return new ConditionObject();
}
}
@Override
public void lock() { sync.acquire(1); }
@Override
public void unlock() { sync.release(1); }
@Override
public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
@Override
public boolean tryLock() { return sync.tryAcquire(1); }
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public Condition newCondition() { return sync.newCondition(); }
}
5.2 共享锁示例
// 简单的读-写锁(仅读锁共享)
class SharedLock extends AbstractQueuedSynchronizer {
SharedLock() {
setState(MAX_SHARED); // 初始所有许可证可用
}
private static final int MAX_SHARED = 10;
@Override
protected int tryAcquireShared(int arg) {
for (;;) {
int current = getState();
int remain = current - arg;
if (remain < 0 || compareAndSetState(current, remain)) {
return remain;
}
}
}
@Override
protected boolean tryReleaseShared(int arg) {
for (;;) {
int current = getState();
int next = current + arg;
if (compareAndSetState(current, next)) {
return true;
}
}
}
}
六、同步器选型指南
| 需求 | 同步器 | 说明 |
|---|---|---|
| 等待 N 个任务完成 | CountDownLatch | 一次性,主线程等待子线程 |
| 多线程互相等待 | CyclicBarrier | 可重用,线程间同步 |
| 限制并发数 | Semaphore | 许可证机制 |
| 互斥访问 | ReentrantLock | 独占锁 |
| 读写分离 | ReentrantReadWriteLock | 读写锁 |
| 自定义同步逻辑 | AQS | 继承实现 |
总结
| 组件 | 基于 | 核心机制 | 可重用 | 典型场景 |
|---|---|---|---|---|
| AQS | - | state + CLH 队列 | - | 框架基石 |
| CountDownLatch | AQS 共享 | state 递减 | ❌ | 等待初始化完成 |
| CyclicBarrier | Lock+Condition | 计数+屏障 | ✅ | 多阶段并行计算 |
| Semaphore | AQS 共享 | 许可证 | ✅ | 限流/资源池 |

1360

被折叠的 条评论
为什么被折叠?



