AQS在锁实现中的应用详解

AQS在锁实现中的应用详解

一、ReentrantLock 实现原理
1. 核心设计
  • 独占锁:基于AQS独占模式实现
  • 可重入:支持同一线程多次获取锁
  • 公平/非公平模式 :通过内部类FairSyncNonfairSync实现
2. AQS状态映射
  • state含义:锁的重入计数
    • 0:锁未被占用
    • >0:锁被占用的次数(重入次数)
3. 核心实现代码

非公平锁获取(NonfairSync.tryAcquire)

java 复制代码
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 1. 锁未被占用,直接CAS获取
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 2. 锁已被当前线程占用,重入计数+1
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // 溢出检查
            throw new Error("Maximum lock count exceeded");
        setState(nextc); // 已持有锁,无需CAS
        return true;
    }
    return false; // 锁被其他线程占用
}

公平锁获取(FairSync.tryAcquire)

java 复制代码
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 公平性体现:检查CLH队列是否有等待的线程
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        // 重入逻辑与非公平锁相同
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

锁释放(Sync.tryRelease)

java 复制代码
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) { // 重入计数为0,释放锁
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
二、CountDownLatch 实现原理
1. 核心设计
  • 共享锁:基于AQS共享模式实现
  • 一次性:计数器减为0后不可重置
  • 多线程等待:多个线程可等待同一个计数器
2. AQS状态映射
  • state含义:计数器值
    • 初始化为构造函数传入的计数
    • 调用countDown()时,state递减
    • 当state=0时,所有等待线程被唤醒
3. 核心实现代码

共享锁获取(tryAcquireShared)

java 复制代码
protected int tryAcquireShared(int acquires) {
    // 计数器为0时,获取锁成功(返回1);否则失败(返回-1)
    return (getState() == 0) ? 1 : -1;
}

共享锁释放(tryReleaseShared)

java 复制代码
protected boolean tryReleaseShared(int releases) {
    // 循环CAS递减计数器,直到为0
    for (;;) {
        int c = getState();
        if (c == 0)
            return false; // 计数器已为0,无需处理
        int nextc = c - 1;
        if (compareAndSetState(c, nextc))
            return nextc == 0; // 计数器减为0时,返回true,唤醒所有等待线程
    }
}
三、Semaphore 实现原理
1. 核心设计
  • 共享锁:基于AQS共享模式实现
  • 许可机制 :通过state控制可用资源数
  • 公平/非公平模式:与ReentrantLock类似
2. AQS状态映射
  • state含义:可用许可数
    • 初始化为构造函数传入的许可数
    • 调用acquire()时,state递减(获取许可)
    • 调用release()时,state递增(释放许可)
3. 核心实现代码

非公平获取许可(NonfairSync.tryAcquireShared)

java 复制代码
protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
}

final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        // 许可不足或CAS失败时,返回负数(获取失败)
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

公平获取许可(FairSync.tryAcquireShared)

java 复制代码
protected int tryAcquireShared(int acquires) {
    for (;;) {
        // 公平性体现:检查CLH队列是否有等待的线程
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

释放许可(Sync.tryReleaseShared)

java 复制代码
protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // 溢出检查
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true; // 释放成功,唤醒后续等待线程
    }
}
四、CountDownLatch 与 CyclicBarrier 的区别(面试重点)
对比维度 CountDownLatch CyclicBarrier
底层实现 基于AQS共享模式 基于ReentrantLock + Condition
是否可重置 不可重置(一次性) 可重置(通过reset()方法)
状态管理 递减计数器(state从N→0) 递增等待线程数(直到达到parties)
使用场景 一个/多个线程等待其他线程完成 多个线程相互等待,达到屏障后继续执行
等待线程数 等待的线程数不固定(可多可少) 固定数量的线程必须全部到达屏障
唤醒机制 计数器为0时,自动唤醒所有等待线程 最后一个线程到达时,唤醒所有等待线程
异常处理 无特殊异常处理 支持BrokenBarrierException,表示屏障被破坏
扩展功能 不支持回调 支持barrierAction,最后一个线程到达时执行

典型使用场景示例

  • CountDownLatch :主线程等待多个子线程完成初始化后再执行

    java 复制代码
    CountDownLatch latch = new CountDownLatch(3);
    // 3个子线程执行完成后调用countDown()
    // 主线程调用latch.await()等待
  • CyclicBarrier :多个线程执行到某一阶段后,相互等待,然后同时继续执行

    java 复制代码
    CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程到达屏障!"));
    // 每个线程执行到barrier.await()时等待
    // 3个线程都到达后,执行barrierAction,然后继续执行
五、AQS同步器对比总结
同步器 模式 state含义 核心特性 典型场景
ReentrantLock 独占 重入计数 可重入、公平/非公平 替代synchronized,支持更灵活的锁控制
CountDownLatch 共享 计数器值 一次性、多线程等待 主线程等待子线程初始化完成
Semaphore 共享 可用许可数 可多次获取/释放、公平/非公平 限流、资源池控制

面试题:CountDownLatch与CyclicBarrier的区别?

回答模板

CountDownLatch和CyclicBarrier都是Java并发包中用于线程同步的工具类,但它们在底层实现、功能特性和使用场景上有明显区别:

  1. 底层实现不同

    • CountDownLatch基于AQS共享模式实现,通过state变量维护计数器
    • CyclicBarrier基于ReentrantLock和Condition实现,内部维护等待线程数
  2. 是否可重置

    • CountDownLatch是一次性的,计数器减为0后不可重置
    • CyclicBarrier是可重置 的,通过reset()方法可以重新使用
  3. 同步机制

    • CountDownLatch:一个/多个线程等待其他线程完成,计数器从N→0
    • CyclicBarrier:多个线程相互等待,直到所有线程到达屏障点
  4. 等待线程数

    • CountDownLatch:等待的线程数不固定,可多可少
    • CyclicBarrier:必须有固定数量的线程到达屏障
  5. 扩展功能

    • CountDownLatch:无回调功能
    • CyclicBarrier:支持barrierAction,最后一个线程到达时执行回调
  6. 使用场景

    • CountDownLatch:适合主从协作场景,如主线程等待子线程完成初始化
    • CyclicBarrier:适合多线程协作场景,如多个线程分阶段执行任务,每个阶段等待所有线程完成

示例代码对比

java 复制代码
// CountDownLatch示例:主线程等待3个子线程完成
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            // 模拟任务执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            latch.countDown(); // 子线程完成,计数器减1
        }
    }).start();
}
latch.await(); // 主线程等待计数器为0

// CyclicBarrier示例:3个线程相互等待,到达屏障后继续执行
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程到达屏障!"));
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            // 第一阶段任务
            Thread.sleep(1000);
            barrier.await(); // 等待其他线程到达屏障
            // 第二阶段任务
            Thread.sleep(1000);
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }).start();
}

总结

AQS作为Java并发包的核心框架,通过模板方法模式为各种同步器提供了统一的实现基础。ReentrantLock、CountDownLatch、Semaphore等同步器通过重写AQS的tryAcquire/tryRelease等方法,实现了各自独特的同步语义。

CountDownLatch和CyclicBarrier作为常用的线程同步工具,虽然都用于线程等待,但在实现原理、功能特性和使用场景上有本质区别。理解它们的区别,对于正确选择和使用同步工具至关重要,也是面试中的高频考点。

相关推荐
仅此,3 小时前
前端接收了id字段,发送给后端就变了
java·前端·javascript·spring·typescript
hashiqimiya3 小时前
androidstudio模拟器安装apk文件
java
IT枫斗者3 小时前
Java 开发实战:从分层架构到性能优化(Spring Boot + MyBatis-Plus + Redis + JWT)
java·spring boot·sql·mysql·性能优化·架构
spencer_tseng3 小时前
RedisConnectionMonitor.java
java
Rover.x3 小时前
Arthas内存泄露排查
java·后端
艺杯羹3 小时前
掌握Spring Boot配置艺术:从YAML基础到实战进阶
java·spring boot·后端·yaml
Lin_Miao_093 小时前
基于 DataX + DataX-Web 生成报表数据
java·数据库
沉迷技术逻辑3 小时前
微服务架构-网关
java·微服务·架构