芯片元器件
HOME
芯片元器件
正文内容
华丰ai助手 | 2026年Java AQS并发基石深度解析:从原理到面试
发布时间 : 2026-05-13
作者 : 小编
访问数量 : 32
扫码分享至微信

华丰ai助手 | 2026年Java AQS并发基石深度解析:从原理到面试

(共27字,含关键词“华丰ai助手”,标题包含2026年时效信息)


文章正文

一、开篇引入

在Java并发编程的世界里,AbstractQueuedSynchronizer(以下简称AQS)是当之无愧的核心基石-1。从面试必问的ReentrantLock,到日常开发中常用的Semaphore、CountDownLatch,几乎所有JUC包下的高级同步工具类都构建在AQS之上-4

很多开发者只会用这些锁,却说不出其背后的原理。面试官问“AQS和ReentrantLock的关系”时答不上来,提到“公平锁与非公平锁”时概念混淆——这些正是学习的典型痛点。

本文由华丰ai助手整理,将从架构到源码逐步拆解AQS的底层实现原理,结合完整可运行的代码示例和高频面试题,帮你理清逻辑、看懂代码、记住考点,真正建立起从“会用”到“懂原理”的完整知识链路。

本文结构预览:痛点切入→核心概念讲解→关联概念梳理→代码示例→底层原理→面试题→总结。


二、痛点切入:为什么需要AQS?

先看一段“原始”的并发控制代码:

java
复制
下载
public class BadCounter {    private int count = 0;        public void increment() {        // 直接用synchronized也可以,但早期我们可能自己写while循环+CAS        while (!compareAndSet()) { / 自旋,CPU空转 / }    }}

传统方式的痛点

  • 耦合高:锁的逻辑和业务逻辑混在一起,难以复用

  • 扩展性差:换个锁策略(公平/非公平)就要重写一套代码

  • 代码冗余:每个同步工具都要重复实现线程排队、阻塞、唤醒的逻辑

  • 容易出错:忘记唤醒线程 → 队列中的线程永远阻塞

正是在这样的背景下,Doug Lea设计了AQS——一个统一的同步框架,将“排队、阻塞、唤醒”这些公共逻辑抽象出来,开发者只需重写几个关键方法就能实现自定义同步器-4

三、核心概念:AQS(AbstractQueuedSynchronizer)

标准定义AbstractQueuedSynchronizer(简称AQS),是Java并发包(java.util.concurrent.locks)中提供的一个基础框架,用于构建依赖先进先出(FIFO)等待队列的阻塞锁及相关同步器-6

拆解关键词

  • Abstract:抽象类,不能直接实例化,需要子类继承并实现核心方法

  • Queued:核心设计——用队列管理等待获取锁的线程

  • Synchronizer:同步器,管理多线程对共享资源的访问

🎯 生活化类比:银行柜台办理业务

  • state:柜台窗口的状态。0表示空闲,>0表示有客户正在办理

  • CLH队列:排队通道,先来的站在前面

  • Node节点:排队的人,每个人包含“我是谁(Thread)”和“我的状态(waitStatus)”

  • CAS操作:抢窗口时,系统原子性地把窗口状态从“空闲”变为“占用”-3

AQS的核心作用:把“排队、阻塞、唤醒”这些通用逻辑封装好,让上层同步器只关心“怎么判断能不能拿到资源”这一个问题-60

四、关联概念:Node节点

AQS能高效管理等待线程,核心在于其内部类Node。每个等待获取锁的线程,都会被封装成一个Node节点,放入等待队列中-1

Node的核心属性

属性类型说明
threadThread当前节点所代表的等待线程
waitStatusint节点的等待状态(见下表)
prevNode前驱节点指针
nextNode后继节点指针

waitStatus的五个取值及含义

常量含义
CANCELLED1线程取消等待(中断或超时),该节点将被跳过
SIGNAL-1当前节点的后继节点需要被唤醒
CONDITION-2当前节点在Condition条件队列中等待
PROPAGATE-3共享模式下,唤醒操作需要向后传播
初始状态0节点刚入队,尚未设置任何状态

💡 注意:waitStatus的修改不是靠轮询,而是由前驱节点在释放锁时主动设置SIGNAL,再由它调用LockSupport.unpark()唤醒后继-10。这种“前驱通知后继”的设计,是AQS高效性的关键。

五、AQS与Node的关系

  • AQS是“框架” :提供state变量 + 队列管理的模板方法(如acquire、release)

  • Node是“载体” :封装每个等待线程的身份和状态

一句话总结:AQS通过state变量和CLH队列管“秩序”,Node节点是队列里的“排队的个体”。两者配合,一个管全局,一个管个体。

六、代码示例:基于AQS实现一个自定义锁

下面展示如何基于AQS实现一个简单的不可重入独占锁

java
复制
下载
import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class SimpleLock {    // 内部类继承AQS,只需实现tryAcquire和tryRelease    private static class Sync extends AbstractQueuedSynchronizer {                // 尝试获取锁(独占模式)        @Override        protected boolean tryAcquire(int arg) {            // CAS尝试将state从0改为1            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);  // 释放锁,state归零            return true;        }                // 判断当前是否持有锁        @Override        protected boolean isHeldExclusively() {            return getState() == 1;        }    }        private final Sync sync = new Sync();        public void lock() { sync.acquire(1); }      // 获取锁    public void unlock() { sync.release(1); }    // 释放锁}

使用示例

java
复制
下载
public class Main {    private static int counter = 0;    private static final SimpleLock lock = new SimpleLock();        public static void main(String[] args) throws InterruptedException {        Runnable task = () -> {            lock.lock();            try {                counter++;                System.out.println(Thread.currentThread().getName() + " count: " + counter);            } finally {                lock.unlock();            }        };                Thread t1 = new Thread(task);        Thread t2 = new Thread(task);        t1.start(); t2.start();        t1.join(); t2.join();    }}

🔍 执行流程解析

  1. 线程1调用lock()sync.acquire(1)tryAcquire(1) CAS将state从0→1成功 → 获取锁

  2. 线程2调用lock()tryAcquire(1) CAS失败 → AQS将线程2封装成Node加入队列 → 调用LockSupport.park()阻塞

  3. 线程1调用unlock()sync.release(1)tryRelease(1)将state设为0 → AQS唤醒队列中的线程2

  4. 线程2被唤醒 → 重新尝试获取锁 → 获取成功

对比:相比手写自旋锁(CPU空转浪费资源),AQS基于LockSupport.park/unpark实现了真正的阻塞与唤醒,高效且可扩展-1

七、底层原理:AQS的“三根支柱”

理解AQS,只需抓住三根支柱-10

支柱核心元素作用
状态机volatile int state表示同步资源状态(0空闲,>0被占用/重入计数)
等待队列CLH变种队列(双向链表)管理等待线程,FIFO排队
阻塞唤醒LockSupport.park() / unpark()线程真正的阻塞与唤醒

底层技术依赖

  • CAS(Compare-And-Swap) :由Unsafe.compareAndSwapInt实现,保证state状态变更的原子性-3

  • volatile:保证state的修改对所有线程立即可见

  • LockSupport:基于parkunpark实现线程阻塞与唤醒,比传统的wait/notify更灵活

  • 模板方法模式acquire() / release() 定义流程骨架,tryAcquire() / tryRelease() 留给子类实现-27

为什么AQS选择volatile int + CAS,而不是AtomicInteger?
volatile提供最轻量的happens-before保证,且AQS通过Unsafe.compareAndSwapInt直接操作volatile字段,比AtomicInteger多一层封装的写法更高效-19

八、高频面试题

面试题1:AQS的全称是什么?它的核心设计思想是什么?

标准答案

  • 全称:AbstractQueuedSynchronizer(抽象队列同步器)-6

  • 核心设计思想:采用模板方法模式,将同步器的核心算法固定,把具体操作留给子类实现。通过一个volatile int state表示同步状态,一个FIFO等待队列管理线程排队,子类只需重写tryAcquiretryRelease等方法即可实现不同的同步逻辑-35

踩分点:模板方法、state + 队列、子类重写、独占/共享模式。


面试题2:AQS支持哪两种模式?分别举例说明。

标准答案

  • 独占模式:同一时刻只有一个线程能获取资源,如ReentrantLock、ReentrantReadWriteLock的写锁-

  • 共享模式:同一时刻多个线程可以同时获取资源,如Semaphore、CountDownLatch-1

踩分点:能说出两种模式的名称并各举1-2个例子。


面试题3:AQS中acquire方法的流程是怎样的?

标准答案(核心四步):

text
复制
下载
public final void acquire(int arg) {    if (!tryAcquire(arg) &&         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))        selfInterrupt();}
  1. tryAcquire:尝试直接获取锁(由子类实现),成功则结束

  2. addWaiter:获取失败,将当前线程封装成Node节点,CAS加入队列尾部-1

  3. acquireQueued:在队列中自旋,只有当前驱节点是头节点时才尝试获取锁;失败则调用LockSupport.park()阻塞-1

  4. selfInterrupt:若等待过程中被中断,在获取锁后补发中断-1

踩分点:能说出四步流程,关键术语“tryAcquire→入队→自旋→阻塞”。


面试题4:公平锁和非公平锁的区别是什么?

标准答案

对比维度公平锁非公平锁
获取顺序严格按照申请时间顺序(FIFO)允许插队,新线程可先竞争
性能相对较低,上下文切换多相对更高,减少线程挂起唤醒
线程饥饿不会发生可能发生

ReentrantLock默认是非公平锁,可通过new ReentrantLock(true)创建公平锁-26

踩分点:能说出核心区别(排队 vs 插队),理解“非公平锁性能更高”的原因——新线程可能直接拿到刚释放的锁,避免内核态挂起/唤醒的开销。


面试题5:Condition和AQS是什么关系?

标准答案

Condition是AQS的内部类ConditionObject的具体实现。AQS维护两个队列

  • 同步队列:存放因获取锁失败而阻塞的线程

  • 条件队列:每个Condition对象维护一个等待队列,存放因调用await()而等待的线程

调用await()时,线程会完全释放锁并进入条件队列;调用signal()时,线程从条件队列被移入同步队列,重新竞争锁-19

踩分点:区分两种队列、await释放锁、signal转移节点。

九、结尾总结

本文核心回顾

知识点要点
AQS是什么AbstractQueuedSynchronizer,Java并发锁的底层框架
核心组件volatile int state + CLH等待队列 + Node节点
三种关键方法tryAcquire(获取)、tryRelease(释放)、acquireQueued(排队)
两种模式独占模式(ReentrantLock)和共享模式(Semaphore)
底层技术CAS、volatile、LockSupport、模板方法模式

⚠️ 易错点提醒

  1. AQS是抽象类,不能直接new AQS(),必须通过子类使用

  2. state的修改必须用compareAndSetState做原子更新,不能直接赋值

  3. 释放锁后必须唤醒后继线程,否则队列中的线程会永远阻塞

AQS是理解Java并发编程的“敲门砖”。掌握了它,ReentrantLock、Semaphore、CountDownLatch等并发工具的实现原理对你来说都将一目了然。

本文由华丰ai助手整理,希望能帮助你在Java并发学习的道路上更进一步。如有疑问或建议,欢迎交流讨论。


王经理: 180-0000-0000(微信同号)
10086@qq.com
北京海淀区西三旗街道国际大厦08A座
©2026  上海羊羽卓进出口贸易有限公司  版权所有.All Rights Reserved.  |  程序由Z-BlogPHP强力驱动
网站首页
电话咨询
微信号

QQ

在线咨询真诚为您提供专业解答服务

热线

188-0000-0000
专属服务热线

微信

二维码扫一扫微信交流
顶部