JUC包里的同步组件主要实现了AQS的哪些主要方法

要理解 JUC(java.util.concurrent)包中同步组件的底层逻辑,核心是它们都基于AbstractQueuedSynchronizer(AQS)框架 ------AQS 提供了通用的同步状态管理、CLH 队列(等待队列)和模板方法,而具体组件只需重写 AQS 的关键抽象方法,即可实现自定义的同步语义。

一、先明确 AQS 的核心角色

AQS 是一个抽象基类,定义了同步组件的通用结构:

  • 同步状态(state :用 volatile int表示同步状态(如锁的重入次数、计数器的剩余值)。
  • CLH 队列 :双向链表实现的等待队列,存储未获取到同步状态的线程。
  • 节点状态(waitStatus :表示节点的等待状态(如 CANCELLEDSIGNALCONDITION等)。
  • 模板方法 :如 acquire(int arg)(独占获取)、release(int arg)(独占释放)、acquireShared(int arg)(共享获取)、releaseShared(int arg)(共享释放)------这些方法调用需要子类重写的抽象方法 完成具体逻辑。

二、JUC 组件重写的 AQS 核心方法

不同 JUC 组件(如锁、计数器、信号量)对应独占模式共享模式,因此重写的 AQS 方法也不同。以下是典型组件的实现分析:

1. 独占模式组件(如 ReentrantLockThreadPoolExecutor.Worker

独占模式用于"一次只有一个线程持有资源"的场景,需重写:

  • tryAcquire(int arg) :尝试获取独占同步状态(成功返回 true,失败返回 false)。
  • tryRelease(int arg) :尝试释放独占同步状态(成功返回 true,失败返回 false)。
  • isHeldExclusively() :判断当前线程是否独占同步状态(用于条件变量)。
示例 1:ReentrantLock(可重入独占锁)

ReentrantLock内部通过 Sync抽象类继承 AQS,再派生出 NonfairSync(非公平锁)和 FairSync(公平锁),重写以下方法:

  • tryAcquire(int acquires)

    尝试获取锁(acquires=1表示获取一次锁)。

    • 非公平锁:直接通过 CAS 修改 state(若 state==0则获取成功,否则判断是否是当前线程重入)。

    • 公平锁:先检查 CLH 队列中是否有前驱节点(保证先到先得),再尝试 CAS 修改 state

      // NonfairSync 的 tryAcquire
      protected final boolean tryAcquire(int acquires) {
      final Thread current = Thread.currentThread();
      int c = getState();
      if (c == 0) {
      if (compareAndSetState(0, acquires)) { // CAS 抢锁
      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;
      }

  • tryRelease(int releases)

    释放锁(releases=1表示释放一次)。减少 state,若 state变为 0,则彻底释放锁,并设置 exclusiveOwnerThreadnull

    复制代码
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
  • isHeldExclusively() :判断当前线程是否持有锁(用于 Condition的等待队列)。

示例 2:ThreadPoolExecutor.Worker(工作线程的独占锁)

Worker继承 AbstractQueuedLongSynchronizer(AQS 的长整型版本),用于实现不可重入的独占锁

  • tryAcquire(long arg) :只有当前线程是 Worker自身时才能获取锁(防止并发修改任务)。
  • tryRelease(long arg) :释放锁(将 state置 0)。
2. 共享模式组件(如 CountDownLatchSemaphoreCyclicBarrier

共享模式用于"多个线程可同时持有资源"的场景,需重写:

  • tryAcquireShared(int arg) :尝试获取共享同步状态(返回 >=0表示成功,<0表示失败)。
  • tryReleaseShared(int arg) :尝试释放共享同步状态(成功返回 true,失败返回 false)。
  • getSharedQueuedThreads() (可选):获取等待队列中的共享线程(用于监控)。
示例 1:CountDownLatch(闭锁)

CountDownLatch内部通过 Sync继承 AQS,state初始化为计数器 count

  • tryAcquireShared(int acquires)

    尝试获取共享锁(即判断计数器是否为 0)。若 state == 0,返回 1(成功);否则返回 -1(失败,进入等待队列)。

    复制代码
    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }
  • tryReleaseShared(int releases)

    释放共享锁(减少计数器)。通过 CAS 循环将 state减 1,若 state变为 0,则唤醒所有等待的共享线程。

    复制代码
    protected boolean tryReleaseShared(int releases) {
        for (;;) {
            int c = getState();
            if (c == 0) return false; // 已经是 0,无需释放
            int nextc = c - 1;
            if (compareAndSetState(c, nextc))
                return nextc == 0; // 若减到 0,返回 true 唤醒其他线程
        }
    }
示例 2:Semaphore(信号量)

Semaphore内部通过 Sync继承 AQS,state表示可用许可数:

  • tryAcquireShared(int acquires)

    尝试获取 acquires个许可(若剩余许可足够,则 CAS 减少许可并返回成功;否则失败)。

    复制代码
    protected int tryAcquireShared(int acquires) {
        for (;;) {
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 || compareAndSetState(available, remaining))
                return remaining;
        }
    }
  • tryReleaseShared(int releases)

    释放 releases个许可(CAS 增加许可,若成功则唤醒等待线程)。

3. 双模式组件(如 ReentrantReadWriteLock

ReentrantReadWriteLock同时支持读锁(共享)写锁(独占) ,内部通过两个 Sync子类实现:

  • 写锁(WriteLock :继承 Sync,重写 tryAcquiretryRelease(独占逻辑)。
    • tryAcquire:判断是否有其他线程持有写锁,或当前线程是否可重入。
  • 读锁(ReadLock :继承 Sync,重写 tryAcquireSharedtryReleaseShared(共享逻辑)。
    • tryAcquireShared:判断是否有写锁持有,或当前线程是否已持有读锁(可重入)。
4. 条件变量(ConditionObject

ConditionObject是 AQS 的内部类,实现 Condition接口,依赖 AQS 的条件队列(单向链表):

  • await() :释放当前线程的同步状态,将节点从同步队列移到条件队列,进入等待。
  • signal() :将条件队列中的第一个节点移回同步队列,唤醒该线程。
  • signalAll() :将条件队列中的所有节点移回同步队列,唤醒所有线程。

三、总结:JUC 组件与 AQS 方法的对应关系

组件类型 典型组件 模式 重写的 AQS 核心方法
独占锁 ReentrantLock 独占 tryAcquiretryReleaseisHeldExclusively
独占锁 ThreadPoolExecutor.Worker 独占 tryAcquiretryRelease
共享锁 CountDownLatch 共享 tryAcquireSharedtryReleaseShared
共享锁 Semaphore 共享 tryAcquireSharedtryReleaseShared
双模式锁 ReentrantReadWriteLock 独占+共享 写锁:tryAcquiretryRelease;读锁:tryAcquireSharedtryReleaseShared
条件变量 ConditionObject ------ 依赖 AQS 的条件队列实现

四、关键结论

JUC 组件的底层逻辑可总结为:

AQS 提供通用框架(状态管理、队列、模板方法),具体组件通过重写 tryAcquire/tryRelease(独占)或 tryAcquireShared/tryReleaseShared(共享)实现自定义同步语义

这种设计让 JUC 组件无需重复造轮子,只需聚焦于"如何获取/释放同步状态",即可快速实现高效、可靠的同步工具。

相关推荐
范纹杉想快点毕业2 小时前
《嵌入式开发硬核指南:91问一次讲透底层到架构》
java·开发语言·数据库·单片机·嵌入式硬件·mongodb
大G的笔记本2 小时前
Java常见设计模式面试题(高频)
java·开发语言·设计模式
老鼠只爱大米2 小时前
Java设计模式之建造者模式(Builder)详解
java·设计模式·建造者模式·builder·23种设计模式
笃行客从不躺平2 小时前
线程池原理复习
java·开发语言
weixin_448771723 小时前
SpringMVC执行流程源码分析之二
java
A尘埃3 小时前
大模型应用python+Java后端+Vue前端的整合
java·前端·python
皮皮林5513 小时前
MinIO 不再“开放”,RustFS 能否成为更优选择?
java
多喝开水少熬夜3 小时前
树与图的深度和广度优先遍历-java实现邻接表存储
java·深度优先·宽度优先
潲爺4 小时前
Java IDEA学习之路:第九周课程笔记归纳
java·学习·intellij-idea