AQS的重入机制和锁释放逻辑

学海无涯,志当存远。燃心砺志,奋进不辍。

愿诸君得此鸡汤,如沐春风,事业有成。

若觉此言甚善,烦请赐赞一枚,共励学途,同铸辉煌!

AQS的基本结构。
AQS通过一个FIFO队列来管理等待线程,并使用一个状态变量(state)来表示锁的状态。
对于重入锁(如ReentrantLock),重入意味着同一个线程可以多次获取锁,而不会导致死锁。
那么,AQS是如何跟踪重入次数的呢?
答案应该与状态变量state有关。每次重入,state会增加,释放时则递减,直到state为0时完全释放锁。接下来,如何释放锁。释放锁的过程应该涉及到将state递减,并且唤醒队列中的后续线程。
但具体步骤是怎样的呢?需要详细说明AQS中tryRelease方法的实现,特别是在ReentrantLock中的应用。需要讲解state的作用、exclusiveOwnerThread的作用,以及重入时如何维护这些变量。
同时,用户可能对释放锁时如何确保完全释放(即state归零)以及如何传递释放信号给其他线程感兴趣。另外,锁释放的问题,比如忘记释放锁导致死锁,或者不理解为什么需要多次unlock。
因此,回答中需要强调重入锁的释放次数必须与获取次数匹配,否则可能导致状态不一致。可能的误区包括:认为每次lock()后只需一次unlock(),而实际上需要相同次数的unlock()。
此外,用户可能不清楚AQS是如何处理非重入锁和重入锁的区别的,因此需要对比说明。代码示例,比如ReentrantLock中的Sync类如何实现tryAcquire和tryRelease,
以及这些方法如何操作state和exclusiveOwnerThread。
同时,应该解释AQS的队列管理机制在释放锁时如何唤醒后续线程。最后,总结重入机制的关键点和释放锁的正确方式,帮助用户巩固理解,避免常见错误。

在Java并发框架中,AbstractQueuedSynchronizerAQS)是实现锁和其他同步器的核心基类。其重入机制锁释放逻辑是理解可重入锁(如ReentrantLock)的关键。以下是详细说明:


一、AQS的重入机制实现

重入(Reentrancy)指同一个线程可以多次获取同一把锁,而不会导致死锁

AQS通过以下方式实现重入:

  1. 线程持有状态(state:AQS使用一个state变量来表示锁的持有状态。在独占锁(如ReentrantLock)的情况下,state变量记录锁被持有的次数。当一个线程尝试获取锁时,AQS会检查当前线程是否已经持有锁(即exclusiveOwnerThread == currentThread)。如果是同一个线程,则允许该线程再次获取锁,表示“可重入”。
  2. 重入计数:每次同一个线程获取锁时,state变量会递增,表示锁的重入次数。相应地,每次释放锁时,state变量会递减,直到state变为0时,锁才会真正释放。
  3. 线程引用:AQS通过内部的一个线程引用exclusiveOwnerThread来跟踪当前持有锁的线程。当一个线程第一次获取锁时,AQS会将exclusiveOwnerThread设置为该线程,并将state从0设置为1。如果同一线程再次尝试获取锁,AQS看到exclusiveOwnerThread已经是当前线程,于是允许锁的重入,并将state递增。
1. 状态变量 state
  • state 表示锁的持有次数:

    • state = 0:锁未被任何线程持有

    • state > 0:锁被某个线程持有,数值表示重入次数

  • 关键代码(以ReentrantLock为例):

    final static class Sync extends AbstractQueuedSynchronizer {// 尝试获取锁(重入逻辑)protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) { // 锁未被持有if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current); // 设置独占线程return true;}} else if (current == getExclusiveOwnerThread()) { // 当前线程已持有锁(重入)setState(c + acquires); // 增加重入次数return true;}return false;}
    }
2. 独占线程标记 exclusiveOwnerThread
  • 记录当前持有锁的线程。

  • 重入时检查线程一致性,确保只有持有锁的线程能再次获取。


二、锁的释放过程

释放锁需要匹配获取次数,AQS通过tryRelease实现:

  1. 修改状态:首先,线程会修改AQS的state状态,将其递减。如果递减后state变为0,则表示锁已经完全释放。
  2. 通知等待线程:接着,线程会检查队列是否为空以及队头的waitStatus是否为0。如果waitStatus不为0(通常表示有线程在等待),则线程会通知队列中的下一个线程来获取锁。这是通过unparkSuccessor方法实现的,该方法会找到队列中离队头最近的、状态合适的节点,并将其唤醒。
1. 释放锁的核心逻辑
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; // 返回是否彻底释放
}
2. 释放后的队列处理
  • tryRelease返回true(即state归零),AQS会唤醒等待队列中的下一个线程

  • 代码触发点

    public final boolean release(int arg) {if (tryRelease(arg)) { // 完全释放Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h); // 唤醒后续线程return true;}return false;
    }

三、重入与释放的匹配规则

操作重入次数变化锁状态
lock()state += 1被当前线程持有
unlock()state -= 1state=0则完全释放
关键要求必须成对调用否则导致死锁或状态异常

四、示例代码:重入锁的获取与释放

ReentrantLock lock = new ReentrantLock();lock.lock();  // state=1
try {// 临界区代码...lock.lock();   // 重入,state=2try {// 嵌套临界区代码...} finally {lock.unlock(); // state=1}
} finally {lock.unlock(); // state=0(完全释放)
}

五、常见问题与注意事项

  1. 释放次数不匹配

    • 多调用一次unlock():抛出IllegalMonitorStateException

    • 少调用一次unlock():锁未完全释放,其他线程无法获取

  2. 非重入锁的对比

    • 若AQS不实现重入逻辑,同一线程第二次调用lock()会直接进入等待队列。

  3. 设计意义

    • 支持递归调用(如递归方法中需要加锁)

    • 避免同一线程重复获取锁时的死锁


六、总结

AQS的重入性是通过state变量exclusiveOwnerThread来实现的。它允许同一个线程多次获取锁,并通过维护一个重入计数器state来跟踪锁的持有次数。

当线程释放锁时,它会递减重入计数器并检查是否需要通知等待线程来获取锁。

  • 重入机制通过state计数和线程独占标记实现。

  • 释放锁时递减state,归零后唤醒其他线程。

  • 必须严格匹配lock()unlock()的调用次数。

学海无涯,志当存远。燃心砺志,奋进不辍。

愿诸君得此鸡汤,如沐春风,事业有成。

若觉此言甚善,烦请赐赞一枚,共励学途,同铸辉煌!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【日常笔记 1】 有关异常学习笔记

今天笔记内容详见 ----- C11_5 异常部分 笔记较乱 , 笔者只是为了记录重要知识点 , 想重点了解相关知识点的可关注笔者正文栏目 ~ 笔者代码仓 : C11_5 代码 异常部分学习笔记 异常基本关键字信息   throw    ----    抛出异常   try - catch ----    捕获异常 , 必须…

Linux UDP网络编程套接字sockets

目录 一、预备知识 1、IP地址 2、端口号 3、Socket网络通信 4、认识TCP/UDP协议 (1)TCP协议 (2)UDP协议 (3)网络字节序 二、socket网络套接字 1、概念 2、Socket 的地址结构和一系列转换函数 &a…

VUE3项目VITE打包优化

VUE3项目VITE打包优化 代码加密依赖配置效果对比图 自动导入依赖配置 代码压缩依赖配置效果对比图 图片压缩依赖配置效果对比图 字体压缩总结与实践运用效果 代码加密 依赖 npm install -D vite-plugin-bundle-obfuscator配置 import vitePluginBundleObfuscator from "…

NO.57十六届蓝桥杯备战|基础算法-高精度|加减乘除|模拟竖式计算(C++)

当数据的值特别⼤,各种类型都存不下的时候,此时就要⽤⾼精度算法来计算加减乘除: 先⽤字符串读⼊这个数,然后⽤数组逆序存储该数的每⼀位;利⽤数组,模拟加减乘除运算的过程。 ⾼精度算法本质上还是模拟算法…

最新DeepSeek-V3-0324:AI模型性能提升与新特性解析

文章目录 性能提升概览新特性解析1. 推理任务表现提高2. 前端开发能力增强3. 中文写作与搜索能力优化4. 模型开源 总结与展望 随着人工智能技术的快速发展,模型的迭代更新成为推动技术进步的重要力量。最近,DeepSeek团队发布了其V3模型的最新小版本更新—…

linux常用指令(7)

今天还是继续学习linux相关的指令,基础越牢固,就越有利于我们后面的学习,那么话不多说,来看. 1.head指令 功能描述:用于显示文件的开头部分内容,默认情况下head显示文件的前10行内容. 基本语法:head 文件 选项:-n nums 显示前nums行内容 …

数仓架构告别「补丁」时代!全新批流一体 Domino 架构终结“批流缝合”

在数字化转型的浪潮中,企业对数据处理的需求日益复杂多变,传统的批处理和流处理架构已难以满足日益增长的性能和时效性要求。在此背景下,YMatrix CEO 姚延栋发布了深度文章《数仓架构告别「补丁」时代!全新批流一体 Domino 架构终…

HTB 笔记 | SQL 注入基础 + 实操小练习 P2

1. 数据库类型 数据库分为两类: 关系型数据库(Relational Databases) 使用表格存储数据(行和列)。数据通过“键”连接,形成逻辑关系。示例:MySQL、PostgreSQL、SQL Server。特点:结…

MySQL 入门大全:数据类型

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…

解决 Not allowed to load local resource 问题

记录一下遇到的问题&#xff1a;html跳转本地资源&#xff0c;用相对路径 这样是不对的&#xff0c;要用 <script src"/jquery.min.js"></script> 网络路径也行&#xff0c;慢了一点 记得一定要关闭浏览器的广告屏蔽器 绝对路径也行&#xff0c;不过要…

STM32实现智能温控系统(暖手宝):PID 算法 + DS18B20+OLED 显示,[学习 PID 优质项目]

一、项目概述 本文基于 STM32F103C8T6 单片机&#xff0c;设计了一个高精度温度控制系统。通过 DS18B20 采集温度&#xff0c;采用位置型 PID 算法控制 PWM 输出驱动 MOS 管加热Pi膜&#xff0c;配合 OLED 实时显示温度数据。系统可稳定将 PI 膜加热至 40℃&#xff0c;适用于…

[深度学习]图像分类项目-食物分类

图像分类项目-食物分类(监督学习和半监督学习) 文章目录 图像分类项目-食物分类(监督学习和半监督学习)项目介绍数据处理设定随机种子读取文件内容图像增广定义Dataset类 模型定义迁移学习 定义超参Adam和AdamW 训练过程半监督学习定义Dataset类模型定义定义超参训练过程 项目介…

C++初阶入门基础二——类和对象(中)

1类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。一个类&#xff0c;我们不写的情况下编译器会默认生成以下6个默认成员函数&#xff0c;需要注意的是这6个中最重要的是前4个&#xff0c;最后两个取地址重载不重…

基于SSM框架的线上甜品销售系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管理就很关键。因此网上销售信息的…

3.25学习总结java 接口+内部类

JDK8以后新增的方法 可以将接口中静态方法和抽象方法中重复的部分抽离出来&#xff0c;作为私有方法&#xff0c;用去private修饰&#xff0c;此方法只为接口提供服务&#xff0c;不需要外界访问。 接口的应用 接口代表规则&#xff0c;是行为的抽象&#xff0c;想让哪个类拥有…

Linux--环境变量

ok&#xff0c;今天我们来学习Linux中的环境变量、地址空间、虚拟内存 环境变量 基本概念 环境变量(environmentvariables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我…

Java 集合 List、Set、Map 区别与应用

一、核心特性对比 二、底层实现与典型差异 ‌List‌ ‌ArrayList‌&#xff1a;动态数组结构&#xff0c;随机访问快&#xff08;O(1)&#xff09;&#xff0c;中间插入/删除效率低&#xff08;O(n)&#xff09;‌‌LinkedList‌&#xff1a;双向链表结构&#xff0c;头尾操作…

基于 arco 的 React 和 Vue 设计系统

arco 是字节跳动出品的企业级设计系统&#xff0c;支持React 和 Vue。 安装模板工具 npm i -g arco-cli创建项目目录 cd someDir arco init hello-arco-pro? 请选择你希望使用的技术栈React❯ Vue? 请选择一个分类业务组件组件库Lerna Menorepo 项目❯ Arco Pro 项目看到以…

JVM-GC(G1)实践—GC异常定位、参数调整、GC更换

前言 如SpringBoot官方介绍所说的那样&#xff0c;从SpringBoot3.x开始支持的最低JDK版本为&#xff1a;JDK17&#xff08;官方推荐使用BellSoft Liberica JDK&#xff09;&#xff0c;其对应的GC为G1。 本文笔者从应用实践的角度出发&#xff0c;记录一些关于GC的一些实践总…

吾爱出品,文件分类助手,高效管理您的 PC 资源库

在日常使用电脑的过程中&#xff0c;文件杂乱无章常常让人感到困扰。无论是桌面堆积如山的快捷方式&#xff0c;还是硬盘中混乱的音频、视频、文档等资源&#xff0c;都急需一种高效的整理方法。文件分类助手应运而生&#xff0c;它是一款文件管理工具&#xff0c;能够快速、智…