Java AQS 原理与同步器实战:CountDownLatch / CyclicBarrier / Semaphore

引言

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

对比CountDownLatchCyclicBarrier
计数方向递减到 0等待到 N
可重用❌ 一次性✅ 可循环
等待方主线程等待子线程线程间互相等待
栅栏动作支持 barrierAction
异常处理countDown 在 finallyBrokenBarrierException
实现原理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 队列-框架基石
CountDownLatchAQS 共享state 递减等待初始化完成
CyclicBarrierLock+Condition计数+屏障多阶段并行计算
SemaphoreAQS 共享许可证限流/资源池
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cheng自牧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值