面试基础---深入解析 AQS

news2025/2/27 22:40:42

深入解析 AQS:从源码到实践,剖析 ReentrantLock 和 Semaphore 的实现

引言

在 Java 并发编程中,AbstractQueuedSynchronizer(AQS)是一个核心框架,它为构建锁和其他同步器提供了基础支持。ReentrantLockSemaphore 是 AQS 的两个典型实现,分别用于实现可重入锁和信号量。本文将从底层源码的角度,深入分析 AQS 的核心机制,并结合 ReentrantLockSemaphore 的实际应用场景,探讨其设计思想与实现细节。


一、AQS 的核心机制

1.1 AQS 的设计思想

AQS 的核心思想是通过一个 FIFO 队列(CLH 队列)来管理线程的排队和唤醒机制,同时结合一个 state 变量来表示同步状态。AQS 提供了两种模式:

  • 独占模式:同一时刻只有一个线程可以获取资源(如 ReentrantLock)。
  • 共享模式:多个线程可以同时获取资源(如 Semaphore)。

1.2 核心数据结构

1.2.1 state 变量

state 是 AQS 的核心变量,用于表示同步状态。例如:

  • ReentrantLock 中,state 表示锁的重入次数。
  • Semaphore 中,state 表示剩余的许可数。
private volatile int state; // 同步状态
1.2.2 CLH 队列

AQS 使用 CLH 队列(Craig, Landin, and Hagersten 锁队列)来管理等待线程。每个线程被封装为一个 Node 节点,节点中保存了线程的状态(如等待、取消)以及前驱和后继节点的引用。

static final class Node {
    volatile int waitStatus; // 线程状态
    volatile Node prev;      // 前驱节点
    volatile Node next;      // 后继节点
    volatile Thread thread;  // 等待线程
}

1.3 关键方法

1.3.1 acquire(int arg)

acquire 是获取资源的核心方法。它的主要逻辑如下:

  1. 调用 tryAcquire 尝试获取资源。
  2. 如果获取失败,将当前线程封装为 Node 并加入 CLH 队列。
  3. 通过自旋和 LockSupport.park() 挂起线程,等待唤醒。
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
1.3.2 release(int arg)

release 是释放资源的核心方法。它的主要逻辑如下:

  1. 调用 tryRelease 尝试释放资源。
  2. 如果释放成功,唤醒 CLH 队列中的下一个线程。
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

二、ReentrantLock 的实现

2.1 公平锁与非公平锁

ReentrantLock 支持公平锁和非公平锁两种模式:

  • 公平锁:严格按照 CLH 队列的顺序获取锁。
  • 非公平锁:允许插队,新线程可以直接尝试获取锁。
2.1.1 公平锁的实现

公平锁在 tryAcquire 中会检查是否有前驱节点,如果有则放弃获取锁。

protected final boolean tryAcquire(int acquires) {
    if (getState() == 0 && !hasQueuedPredecessors() &&
        compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(currentThread());
        return true;
    }
    return false;
}
2.1.2 非公平锁的实现

非公平锁在 tryAcquire 中不会检查前驱节点,直接尝试获取锁。

final boolean nonfairTryAcquire(int acquires) {
    if (getState() == 0 && compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(currentThread());
        return true;
    }
    return false;
}

2.2 tryLock()lock() 方法

  • lock():调用 acquire(1),如果获取失败则进入等待队列。
  • tryLock():调用 tryAcquire(1),立即返回是否获取成功。

三、Semaphore 的实现

3.1 信号量的许可数管理

Semaphore 通过 state 变量表示剩余的许可数。acquire()release() 方法分别用于获取和释放许可。

3.1.1 acquire()

acquire 方法会减少 state 的值,如果许可不足则进入等待队列。

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
3.1.2 release()

release 方法会增加 state 的值,并唤醒等待线程。

public void release() {
    sync.releaseShared(1);
}

四、实际应用场景

4.1 ReentrantLock 的应用

  • 高并发环境下的资源竞争控制:如数据库连接池的并发访问。
  • 可重入特性:支持同一线程多次获取锁,避免死锁。

4.2 Semaphore 的应用

  • 线程池的任务调度:通过信号量限制并发任务数。
  • 限流:控制系统的并发请求数,防止资源耗尽。

五、性能优化和注意事项

5.1 性能优化

  • 自旋锁:在短时间内通过自旋尝试获取锁,减少线程切换的开销。
  • CAS 操作:通过 compareAndSetState 实现无锁化的状态更新。

5.2 注意事项

  • 避免死锁:确保锁的获取和释放成对出现。
  • 合理设置超时:使用 tryLock(long timeout, TimeUnit unit) 避免长时间等待。

六、总结与展望

AQS 是 Java 并发包的基石,其设计思想(CLH 队列 + state 变量)为构建高效、灵活的同步器提供了强大支持。ReentrantLockSemaphore 是 AQS 的典型应用,分别解决了独占资源和共享资源的同步问题。

未来,AQS 可能会在以下方面进一步优化:

  1. 更高效的自旋策略。
  2. 对 NUMA 架构的更好支持。
  3. 更灵活的同步模式扩展。

附录

图表:CLH 队列结构

Head -> Node1 -> Node2 -> Node3 -> Tail

在这里插入图片描述

关键代码片段

  • AQS 的 acquirerelease 方法。
  • ReentrantLock 的公平锁与非公平锁实现。
  • Semaphoreacquirerelease 方法。

通过本文的分析,相信读者能够深入理解 AQS 的设计思想及其在 ReentrantLockSemaphore 中的应用,为高并发编程打下坚实基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2307069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从 0 到 1,用 Python 构建超实用 Web 实时聊天应用

从 0 到 1,用 Python 构建超实用 Web 实时聊天应用 本文深入剖析如何运用 Python 的 Flask 框架与 SocketIO 扩展,搭建一个功能完备的 Web 实时聊天应用。从环境搭建、前后端代码实现,到最终运行展示,逐步拆解关键步骤&#xff0…

Vue2+Element实现Excel文件上传下载预览【超详细图解】

目录 一、需求背景 二、落地实现 1.文件上传 图片示例 HTML代码 业务代码 2.文件下载 图片示例 方式一:代码 方式二:代码 3.文件预览 图片示例 方式一:代码 方式二:代码 一、需求背景 在一个愉快的年后&#xff…

[记录贴] 火绒奇怪的进程保护

最近一次更新火绒6.0到最新版,发现processhacker的结束进程功能无法杀掉火绒的进程,弹窗提示如下: 可能是打开进程时做了权限过滤,火绒注册了两个回调函数如下: 但奇怪的是,在另外一台机器上面更新到最新版…

【蓝桥杯】每天一题,理解逻辑(1/90)【Leetcode 移动零】

文章目录 题目解析讲解算法原理【双指针算法思路】(数组下标充当指针)如何划分和执行过程大致 代码详情 题目解析 题目链接:https://leetcode.cn/problems/move-zeroes/description/ 题目意思解析 把所有的零移动到数组的末尾保持非零元素的相对顺序 理解了这两层…

pycharm远程连接服务器运行pytorch

Linux部署pytorch 背景介绍 不同的开源代码可能需要不同的实验环境和版本,这时候的确体现出Anaconda管理环境的好处了,分别搞一个独立环境方便管理。 有的教程建议选择较旧的版本,但笔者建议在条件允许的情况下安装最新版,本次…

java练习(41)

ps:题目来自力扣 最接近的三数之和 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 import java.util.Arrays;class Solut…

PDF扫描档智能方向识别:多模型投票机制的实践测试 救活古典书籍

2025-02-22 20:10物联全栈123 尊敬的诸位!我是一名物联网工程师。关注我,持续分享最新物联网与AI资讯和开发实战。期望与您携手探寻物联网与 AI 的无尽可能 RAG知识库搭建的过程中,扫描档pdf的支持和准确率一直是个大家都不愿主动提起的事情…

轻松搭建:使用Anaconda创建虚拟环境并在PyCharm中配置

一、使用Anaconda创建虚拟环境 1. 安装Anaconda 2..conda常用的命令 3. 创建虚拟环境-以搭建MachineVision为例 4. 激活虚拟环境 5. 安装依赖包 二、PyCharm配置环境 在进行Python项目开发时,合理的环境管理是必不可少的,特别是当你在多个项目中…

驱动开发系列39 - Linux Graphics 3D 绘制流程(二)- 设置渲染管线

一:概述 Intel 的 Iris 驱动是 Mesa 中的 Gallium 驱动,主要用于 Intel Gen8+ GPU(Broadwell 及更新架构)。它负责与 i915 内核 DRM 驱动交互,并通过 Vulkan(ANV)、OpenGL(Iris Gallium)、或 OpenCL(Clover)来提供 3D 加速。在 Iris 驱动中,GPU Pipeline 设置 涉及…

MinIO整合SpringBoot实现文件上传、下载

文章目录 配置1. 部署MinIO服务2. 整合SpringBoot 功能实现1. 文件上传2. 文件下载 总结 配置 1. 部署MinIO服务 这里以docker为例: 安装minio命令docker run -p 9000:9000 -p 9001:9001 \ --name minio \ -v /path/to/data:/data \ -e "MINIO_ROOT_USERmin…

FreeRTOS(3)列表List

在 FreeRTOS 的源码中大量地使用了列表和列表项,因此想要深入学习 FreeRTOS,列表和列表项是必备的基础知识。这里所说的列表和列表项,是 FreeRTOS 源码中 List 和 List Item 的 直译,事实上, FreeRTOS 中的列表和列表项…

C++和OpenGL实现3D游戏编程【连载23】——几何着色器和法线可视化

欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 1、本节实现的内容 上一节课,我们在Blend软件中导出经纬球模型时,遇到了经纬球法线导致我们在游戏中模型光照显示问题,我们在Blender软件中可以通过…

Harmony开发笔记(未完成)

一、感想 作为一名拥有11年经验的Android开发者,我亲历了Android从高速发展到如今面临“僧多粥少”的过程。技术的世界瞬息万变,没有一种技术能够让人依赖一辈子。去年初,我自学了鸿蒙系统,并顺利通过了鸿蒙官方的初级和高级认。…

【Java面试】创建线程有哪几种方式

目录 1.继承Thread类 2.实现Runnable接口 3.实现Callable接口和FutureTask 4.使用Executor框架(线程池) Java并发编程中不同接口和类之间的关系 总结 1.继承Thread类 优点: 简单直观。直接继承Thread类,可以方便地使用Threa…

在Linux环境下利用MTCNN进行人脸检测(基于ncnn架构)

概述 本文将详细介绍如何在Linux环境下部署MTCNN模型进行人脸检测,并使用NCNN框架进行推理。 1. CMake的安装与配置 下载CMake源码 前往CMake官网下载,找到适合您系统的最新版本tar.gz文件链接,或者直接通过wget下载:CMake官方…

AI数字人系统源码部署解决方案!!!

一、开场白 如今,科技的步伐越来越快,数字人已经从想象中走进了我们的现实生活。它们在娱乐、教育、医疗等多个领域大放异彩。了解数字人的代码开发技术,能让我们更好地理解其工作原理,为那些想在这一领域大展拳脚或者用数字人技…

W803|联盛德|WM IoT SDK2.X测试|(1)开箱:开发板及说明

前几天关注的联盛德微电子新推出了WM IoT SDK2.X,正式发布后,邀请用户参加“免费试用,赢千元大礼”活动,填写信息,等待统一发送,很快收到了板子。 活动地址:联盛德微电子WM IoT SDK2.X正式发布…

2.✨java练习1(熟悉“类”)

1. A B - AcWing题库 问题描述 输入两个整数&#xff0c;求这两个整数的和是多少。 输入格式 输入两个整数A,B&#xff0c;用空格隔开 输出格式 输出一个整数&#xff0c;表示这两个数的和 数据范围 0≤A,B≤1e8 C #include <iostream> // 包含标准输入输出库 using n…

基本网络安全的实现

基本网络安全的实现 一 &#xff1a;AAA AAA 是Authentication&#xff0c;Authorization and Accounting&#xff08;认证、授权和计费&#xff09;的简 称&#xff0c;它提供了一个用来对认证、授权和计费这三种安全功能进行配置的一致性框架&#xff0c; 它是对网络安全…

快手前端通用静态托管服务KFX演进历程:从崎岖土路到平坦高速

快手静态部署托管服务&#xff08;KFX&#xff09;历经四年发展&#xff0c;经历了三个阶段&#xff0c;一步步从勉强能行车的“崎岖土路”到现在多车道并行的“平坦高速”&#xff0c;这一转变极大地提升了资源利用率和效率&#xff0c;满足业务的实际需要。本文将带你了解其背…