当前位置: 首页 > ds >正文

关于并发编程AQS的学习

目录

1. AQS的核心作用

2. AQS的核心结构

3. 关键方法

4. AQS的应用示例

4.1、ReentrantLock的实现

4.2、CountDownLatch的实现

5. AQS的优势

6. 对比其他同步机制


前言 

       AQS(AbstractQueuedSynchronizer) 是Java并发编程中一个核心的同步器框架,位于 java.util.concurrent.locks 包下,由Doug Lea设计。它是构建锁(如 ReentrantLock)和其他同步工具(如 CountDownLatchSemaphore)的基础抽象类。

如下图所示:

public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {private static final long serialVersionUID = 7373984972572414691L;/*** Creates a new {@code AbstractQueuedSynchronizer} instance* with initial synchronization state of zero.*/protected AbstractQueuedSynchronizer() { }

1. AQS的核心作用

  • 提供模板:通过抽象方法(如 tryAcquiretryRelease)定义同步器的核心逻辑,开发者只需实现这些方法即可自定义锁或同步组件。

  • 管理线程排队:内部通过 CLH队列(一种FIFO的双向链表)实现线程的阻塞和唤醒机制,处理竞争资源的线程排队问题。

  • 支持独占/共享模式

    • 独占模式(Exclusive):同一时刻只有一个线程能获取资源(如 ReentrantLock)。

    • 共享模式(Shared):多个线程可同时获取资源(如 Semaphore)。


2. AQS的核心结构

如下图所示:

  • 状态变量(state
    通过 volatile int state 表示资源状态(如锁的重入次数、信号量的许可证数量)。

  • CLH队列
    存储等待线程的队列,节点(Node)封装线程及等待状态(如 CANCELLEDSIGNAL)。


3. 关键方法

  • 资源获取

    • acquire(int arg):独占模式下获取资源,内部调用 tryAcquire(需子类实现)。

    • acquireShared(int arg):共享模式下获取资源,内部调用 tryAcquireShared

  • 资源释放

    • release(int arg):独占模式释放资源,调用 tryRelease

    • releaseShared(int arg):共享模式释放资源,调用 tryReleaseShared

  • 模板方法(需子类实现):

protected boolean tryAcquire(int arg);  // 独占模式尝试获取资源
protected boolean tryRelease(int arg);  // 独占模式尝试释放资源
protected int tryAcquireShared(int arg); // 共享模式尝试获取资源
protected boolean tryReleaseShared(int arg); // 共享模式尝试释放资源

4. AQS的应用示例

4.1、ReentrantLock的实现

  • state 表示锁的重入次数:

    • 线程首次获取锁时,state 从0变为1。

    • 同一线程重入时,state 递增。

  • 非公平锁直接尝试CAS修改 state,失败后进入队列。

示例1:自定义独占锁(类似ReentrantLock)

import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class MyLock {private final Sync sync = new Sync();// 自定义同步器,继承AQSprivate static class Sync extends AbstractQueuedSynchronizer {// 尝试获取锁(state=0表示未锁定,1表示已锁定)@Overrideprotected boolean tryAcquire(int arg) {if (compareAndSetState(0, 1)) { // CAS操作抢锁setExclusiveOwnerThread(Thread.currentThread()); // 设置当前线程为锁持有者return true;}return false;}// 尝试释放锁@Overrideprotected boolean tryRelease(int arg) {if (getState() == 0) throw new IllegalMonitorStateException();setExclusiveOwnerThread(null); // 清空持有线程setState(0); // 状态置为0return true;}// 是否被当前线程独占@Overrideprotected boolean isHeldExclusively() {return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();}}// 对外暴露的加锁方法public void lock() {sync.acquire(1); // 调用AQS的acquire模板方法}// 对外暴露的解锁方法public void unlock() {sync.release(1); // 调用AQS的release模板方法}
}// 测试代码
public class TestMyLock {private static int count = 0;private static MyLock lock = new MyLock();public static void main(String[] args) throws InterruptedException {Runnable task = () -> {for (int i = 0; i < 10000; i++) {lock.lock();try {count++;} finally {lock.unlock();}}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();t1.join();t2.join();System.out.println("Final count: " + count); // 正确输出20000}
}

关键点

  1. tryAcquire 和 tryRelease 是AQS要求子类实现的模板方法。

  2. 通过CAS操作(compareAndSetState)保证原子性。

  3. acquire 和 release 是AQS提供的模板方法,内部会处理线程排队和唤醒。

4.2、CountDownLatch的实现

  • state 初始化为计数器的值(如N)。

  • await() 调用 acquireShared,当 state=0 时唤醒所有等待线程。

  • countDown() 调用 releaseShared,递减 state

示例2:自定义CountDownLatch

import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class MyCountDownLatch {private final Sync sync;public MyCountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException();this.sync = new Sync(count);}// 自定义同步器(共享模式)private static class Sync extends AbstractQueuedSynchronizer {Sync(int count) {setState(count); // 初始化状态值}// 尝试获取共享资源(state=0时返回1,否则返回-1表示需要阻塞)@Overrideprotected int tryAcquireShared(int acquires) {return getState() == 0 ? 1 : -1;}// 尝试释放共享资源(每次countDown减1)@Overrideprotected boolean tryReleaseShared(int releases) {for (;;) {int current = getState();if (current == 0) return false; // 已经为0,无需操作int next = current - 1;if (compareAndSetState(current, next)) {return next == 0; // 返回true表示所有线程已释放}}}}public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1); // 调用AQS共享模式获取}public void countDown() {sync.releaseShared(1); // 调用AQS共享模式释放}
}// 测试代码
public class TestMyLatch {public static void main(String[] args) throws InterruptedException {MyCountDownLatch latch = new MyCountDownLatch(2);Runnable task = () -> {System.out.println(Thread.currentThread().getName() + " 完成任务");latch.countDown();};new Thread(task, "线程1").start();new Thread(task, "线程2").start();latch.await(); // 主线程等待System.out.println("所有任务完成!");}
}

关键点

  1. tryAcquireShared 返回1表示成功(state=0时唤醒所有等待线程),-1表示需要阻塞。

  2. tryReleaseShared 通过CAS循环递减 state,直到为0时返回true触发唤醒。


5. AQS的优势

  • 复用性:开发者无需从头实现线程排队、阻塞/唤醒机制。

  • 灵活性:通过重写模板方法,可快速构建自定义同步器。

  • 高性能:基于CAS和volatile变量,减少锁竞争开销。


6. 对比其他同步机制


总结

        AQS是Java并发包的基石,理解其原理能更高效地使用 ReentrantLockSemaphore 等工具,或在需要时开发自定义同步器。其核心思想是 “模板方法模式 + 资源状态管理 + 线程排队”


参考文章:

1、谈谈Java多线程离不开的AQS_java aqs-CSDN博客

http://www.xdnf.cn/news/6216.html

相关文章:

  • 为什么go语言中返回的指针类型,不需要用*取值(解引用),就可以直接赋值呢?
  • 什么是函数重载?为什么 C 不支持函数重载,而 C++能支持函数重载?
  • 电商平台自动化
  • 基于 Spring Boot 瑞吉外卖系统开发(十五)
  • 【MoveIt 2】使用 MoveIt 任务构造器(MoveIt Task Constructor)进行拾取和放置
  • Docker 常见问题及其解决方案
  • NLP的基本流程概述
  • uni-app vue3版本打包h5后 页面跳转报错(uni[e] is not a function)
  • 使用ECS搭建云上博客wordpress(ALMP)
  • 零基础用 Hexo + Matery 搭建博客|Github Pages 免费部署教程
  • [操作系统] 策略模式进行日志模块设计
  • OkHttp连接池
  • 5月13日日记
  • 《社交应用动态表情:RN与Flutter实战解码》
  • 场景以及八股复习篇
  • 数据清洗ETL
  • 【Python 算法零基础 2.模拟 ④ 基于矩阵】
  • 【starrocks】StarRocks 常见 HTTP 操作与导入错误排查指南
  • 数值积分知识
  • MK米客方德SD NAND:无人机存储的高效解决方案
  • Qwen-3 模型的应用及实际用例
  • 面向具身智能的视觉-语言-动作模型(VLA)综述
  • 大数据开发 hadoop集群 3.Hadoop运行环境搭建
  • 【Linux】第十六章 分析和存储日志
  • voip--初始化libosip2库
  • 首个专业AI设计Agent发布-Lovart
  • nvrtc环境依赖
  • C++类和对象之相关特性
  • 养猪场巡检机器人的设计与应用研究
  • Python环境管理工具深度指南:pip、Poetry、uv、Conda