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

Java 深度与实战 · 每日一读 :高频面试真题解析 · ReentrantLock / CAS / AQS 篇

ReentrantLock 深层分析:CAS、AQS原理全揭秘

此文为「Java 深度与实战·每日一读」系列第1篇,原创专栏,全篇不含水分,该系列整个面向:初学、进阶、面试、原理、实战,全综合型导向

目标:让任何级别的 Java 学习者,看不懂也要收藏,看得懂就能高速成长!

目录

  • ReentrantLock 深层分析:CAS、AQS原理全揭秘
    • @[toc](目录)
  • 一、基础矩阵:ReentrantLock 的概念
  • 二、CAS 原理完全解析
    • 2.1 CAS 是什么?
    • 2.2 CAS 实现原7406
    • 2.3 CAS 定位的问题:
  • 三、AQS 原理全揭秘
    • 3.1 AQS 是什么?
    • 3.2 AQS 工作流程
    • 3.3 AQS 的两种锁模式
  • 四、ReentrantLock 实现原理
    • 4.1 主要组成
    • 4.2 上锁流程
    • 4.3 重入的原理
  • 五、实战示例:体验一下经典场景
    • 5.1 例子:多线程清单操作
    • 5.2 例子:异常处理
  • 六、深层思考:ReentrantLock 比最原始的锁属性有什么优势?
  • 七、原理其实一点不难:简单就是最大的备考利器
  • 🗂️八、面试问题目录
    • 🔥 高频面试真题解析 · ReentrantLock / CAS / AQS 篇
      • 一、ReentrantLock 相关
        • 1. 什么是 ReentrantLock?与 synchronized 有什么区别?
        • 2. 为什么叫“可重入锁”?它如何实现可重入?
        • 3. ReentrantLock 支持哪些锁类型?怎么设置?
        • 4. 如何正确使用 ReentrantLock,避免死锁?
        • 5. tryLock 有什么应用场景?与 lock() 有什么不同?
        • 6. ReentrantLock 是怎么保证线程安全的?内部用到了哪些底层机制?
      • 二、CAS 相关
        • 1. CAS 原理是什么?CAS 操作是如何保证原子性的?
        • 2. CAS 操作失败会发生什么?什么是自旋?
        • 3. CAS 有哪些典型问题?如何解决 ABA 问题?
        • 4. CAS 和传统的加锁机制相比,有哪些优缺点?
      • 5. Java 中有哪些类用到了 CAS?
    • 三、AQS 相关
      • 1. 什么是 AQS?它在 Java 并发框架中起到了什么作用?
        • 2. AQS 的核心设计是什么?
      • 四、综合类问题(高频+进阶)
        • 1. 如何自己实现一个简单的可重入锁(MyLock)?
        • 2. 高并发场景下,什么时候使用 CAS?什么时候应该使用锁?
  • 🙏 感谢阅读!

一、基础矩阵:ReentrantLock 的概念

ReentrantLock(重入锁),是一种在 JDK1.5 引入的是符合 java.util.concurrent.locks.Lock 接口的实现类,它对 synchronized 进行了很多封装和扩展:

  • 可以手动锁 / 释放,更灵活
  • 支持多种锁(全局锁/全局重入锁)
  • 支持反应不同类型的等待(可不可为)

但是,其实现原理,非常高级!


二、CAS 原理完全解析

2.1 CAS 是什么?

CAS(Compare And Swap):比较并且替换。

通过 CPU 原定命令,把 目标内存地址的值预期值 比较,如果相等,则更新成新值;否则,不做操作。

操作是原定的,一步到位,避免线程竞争。

2.2 CAS 实现原7406

常见实现:

  • x86 系列 CPU :通过 cmpxchg
  • ARM 系列 CPU :使用 LDREX/STREX

JDK 中,主要通过 sun.misc.Unsafe 实现。

Unsafe.compareAndSwapInt(Object obj, long offset, int expect, int update)

2.3 CAS 定位的问题:

问题解决方案
ABA 问题AtomicStampedReference
自旋耗费 CPU合理控制自旋次数
不能操作多个值AtomicReference 或使用锁

三、AQS 原理全揭秘

3.1 AQS 是什么?

AbstractQueuedSynchronizer,抽象队列合并器

基于 FIFO 队列,提供一种构建高级合并器(如:ReentrantLock, CountDownLatch)的通用框架。

核心组件

  • state :锁的状态值(int)
  • CLH 队列 :线程坐标队列
  • Node :表示每个线程

3.2 AQS 工作流程

  1. CAS 抽奖默认拥有者权限
  2. 失败的线程进入队列排队等待
  3. 拥有者释放时,通知队头线程
  4. 队头线程重新挑战 CAS

图示:

线程A(锁住) -> 线程B(等待) -> 线稌C(等待)

它们就排成一条队。

3.3 AQS 的两种锁模式

  • 单种上锁(一个线程拥有)
  • 共享锁(多线程同时拥有,如 ReadWriteLock)

四、ReentrantLock 实现原理

4.1 主要组成

  • Sync:内部抽象类,继承 AQS
  • NonfairSync / FairSync:非公平和公平版本

4.2 上锁流程

  1. 尝试通过 CAS 拥有 state=1
  2. 失败:入队排队,等待
  3. 释放时,把 state=0,并 unpark 队头线程

4.3 重入的原理

同一线程再次上锁,state++,需要释放多次才能真正释放锁。


五、实战示例:体验一下经典场景

5.1 例子:多线程清单操作

Lock lock = new ReentrantLock();public void clearCart() {lock.lock();try {// 清空购物车cart.clear();} finally {lock.unlock();}
}

5.2 例子:异常处理

必须放在 finally 中,否则导致泛锁,系统失效。


六、深层思考:ReentrantLock 比最原始的锁属性有什么优势?

方面synchronizedReentrantLock
解锁自动手动
应急中断不支持支持(lockInterruptibly)
时间等待不支持支持(tryLock)
公平性不保证可选公平

七、原理其实一点不难:简单就是最大的备考利器

只需记住:

  • CAS 保证原定性
  • AQS 通过队列来管理多个线程
  • ReentrantLock 给了更灵活的接口,更符合实际场景

🗂️八、面试问题目录

🔥 高频面试真题解析 · ReentrantLock / CAS / AQS 篇

点击直接跳转查看详细解析👇

  • 一、ReentrantLock 相关
    • 1. 什么是 ReentrantLock?与 synchronized 有什么区别?
    • 2. 为什么叫“可重入锁”?它如何实现可重入?
    • 3. ReentrantLock 支持哪些锁类型?怎么设置?
    • 4. 如何正确使用 ReentrantLock,避免死锁?
    • 5. tryLock 有什么应用场景?与 lock() 有什么不同?
    • 6. ReentrantLock 是怎么保证线程安全的?内部用到了哪些底层机制?
  • 二、CAS 相关
    • 1. CAS 原理是什么?CAS 操作是如何保证原子性的?
    • 2. CAS 操作失败会发生什么?什么是自旋?
    • 3. CAS 有哪些典型问题?如何解决 ABA 问题?
    • 4. CAS 和传统的加锁机制相比,有哪些优缺点?
    • 5. Java 中有哪些类用到了 CAS?
  • 三、AQS 相关
    • 1. 什么是 AQS?它在 Java 并发框架中起到了什么作用?
    • 2. AQS 的核心设计是什么?
    • 3. AQS 支持哪两种模式?分别有哪些代表性实现?
    • 4. AQS 如何实现线程挂起和唤醒?
    • 5. 如何基于 AQS 自定义一个同步器?
  • 四、综合类问题(高频+进阶)
    • 1. 如何自己实现一个简单的可重入锁(MyLock)?
    • 2. 高并发场景下,什么时候使用 CAS?什么时候应该使用锁?
    • 3. 公平锁和非公平锁的区别?使用场景?
    • 4. ReentrantLock 在高并发下会不会出现性能问题?原因是什么?
    • 5. synchronized、Lock、原子类三者怎么选?

一、ReentrantLock 相关

1. 什么是 ReentrantLock?与 synchronized 有什么区别?

ReentrantLock 是一种显式的锁机制,它属于 Java 的 java.util.concurrent 包,提供了比 synchronized 更强大的功能,如公平性设置、可中断等。

  • ReentrantLock 是可重入的,表示一个线程可以多次获取同一把锁,而不至于被自己阻塞。
  • 与 synchronized 区别:
    • ReentrantLock 支持 可中断锁,而 synchronized 不支持。
    • ReentrantLock 可以进行 公平性设置,确保锁被最久等待的线程先获得。
    • ReentrantLock 提供 tryLock() 方法,能设置锁的超时时间,而 synchronized 没有此功能。
2. 为什么叫“可重入锁”?它如何实现可重入?

“可重入”是指一个线程可以多次请求获取同一个锁,而不会发生死锁。线程再次请求时,不会被阻塞,直到线程释放锁。

  • 实现原理:
    • ReentrantLock 内部使用一个计数器来记录获取锁的次数,当前线程每获取一次锁,计数器加1。
    • 当计数器为0时,锁被释放。也就是说,锁的释放是通过计数器控制的。
3. ReentrantLock 支持哪些锁类型?怎么设置?

ReentrantLock 提供两种类型的锁:

  • 公平锁:线程获取锁的顺序为先到先得,适合任务对锁公平性要求较高的场景。
  • 非公平锁:线程获取锁的顺序不保证公平,适合大多数高性能场景。

可以通过 ReentrantLock(true) 设置公平锁,默认为非公平锁。

4. 如何正确使用 ReentrantLock,避免死锁?

死锁通常发生在多个线程相互持有锁且等待对方释放锁的场景。避免死锁的常见策略包括:

  • 避免锁嵌套:减少线程在持有锁的情况下进行其它操作。
  • 锁定顺序:确保多个线程获取多个锁时,锁的顺序一致。
5. tryLock 有什么应用场景?与 lock() 有什么不同?

tryLock() 方法可以设置超时来尝试获取锁,如果在规定时间内获取不到锁,返回 false

  • 应用场景:适用于尝试获取锁的操作,如果获取不到锁,可以继续执行其他任务。
  • 与 lock() 的区别lock() 是阻塞式的,一直等待获取锁;而 tryLock() 是非阻塞的,可以设置等待超时。
6. ReentrantLock 是怎么保证线程安全的?内部用到了哪些底层机制?

ReentrantLock 通过 AQS(AbstractQueuedSynchronizer) 框架实现线程安全。它通过 CAS(Compare And Swap) 技术实现原子操作,避免了竞争条件,并通过队列机制来管理等待线程。


二、CAS 相关

1. CAS 原理是什么?CAS 操作是如何保证原子性的?

CAS 是一种乐观锁机制,它通过比较内存中的值和预期值,如果相同,则更新为新值;如果不同,则不做任何操作。这样保证了多个线程并发执行时,只有一个线程能够成功更新。

  • 原子性保证:CAS 操作的原子性是由硬件支持的(如 CPU 中的原子指令)。
2. CAS 操作失败会发生什么?什么是自旋?
  • CAS 操作失败时,意味着有其他线程对目标变量进行了修改。此时,CAS 操作会重试,直到成功为止。
  • 自旋:CAS 失败后,会进入自旋状态,即线程不会被挂起,而是反复尝试执行 CAS 操作,直到成功或超时。
3. CAS 有哪些典型问题?如何解决 ABA 问题?
  • ABA 问题:在 CAS 操作中,检查到值从 A 变成 B 后,又变回 A,导致线程误以为值没有改变。
  • 解决方法:使用 版本号(即带时间戳的 CAS),通过额外的标记位来解决 ABA 问题。
4. CAS 和传统的加锁机制相比,有哪些优缺点?
  • 优点:CAS 操作不需要加锁,避免了线程上下文切换,提高了并发性能。
  • 缺点:CAS 会导致 ABA 问题,并且在长时间自旋的情况下,可能会导致 CPU 占用过高。

5. Java 中有哪些类用到了 CAS?

Java 中许多类使用 CAS 来优化性能,主要包括:

  • AtomicInteger
  • AtomicReference
  • ReentrantLock
  • ConcurrentLinkedQueue

三、AQS 相关

1. 什么是 AQS?它在 Java 并发框架中起到了什么作用?

AQS(AbstractQueuedSynchronizer)是 Java 提供的一个用于实现同步器的框架。它通过维护一个队列来管理请求同步的线程,从而实现锁、信号量等并发控制结构。

2. AQS 的核心设计是什么?

AQS 的核心设计是一个 FIFO(先入先出)队列,用来存储等待获取锁的线程。每个线程都通过 CAS 操作来尝试获取锁,成功的线程进入临界区,失败的线程进入等待队列。


四、综合类问题(高频+进阶)

1. 如何自己实现一个简单的可重入锁(MyLock)?
class MyLock {private int count = 0;private Thread currentThread = null;public synchronized void lock() {Thread thread = Thread.currentThread();while (currentThread != thread && count > 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}currentThread = thread;count++;}public synchronized void unlock() {if (Thread.currentThread() == currentThread) {count--;if (count == 0) {currentThread = null;notify();}}}
}
2. 高并发场景下,什么时候使用 CAS?什么时候应该使用锁?
  • 使用 CAS:适合无锁操作且对性能要求极高的场景,如计数器更新。
  • 使用锁:适用于有多个线程访问共享资源时,需要保证原子性和数据一致性,且数据量较大时使用锁更安全。

🙏 感谢阅读!

感谢大家阅读!如果你觉得这篇文章对你有所帮助,欢迎:
🔹 点赞
🔹 收藏
🔹 分享给更多需要的朋友

如果你有任何问题或者想深入讨论的内容,欢迎在评论区留言,或者私信我!你的每一条反馈都是我持续创作的动力!💪


相关文章:

Java 深度与实战 · 每日一读 :高频面试真题解析 · ReentrantLock / CAS / AQS 篇

ReentrantLock 深层分析:CAS、AQS原理全揭秘 此文为「Java 深度与实战每日一读」系列第1篇,原创专栏,全篇不含水分,该系列整个面向:初学、进阶、面试、原理、实战,全综合型导向。 目标:让任何级…...

golang goroutine(协程)和 channel(管道) 案例解析

文章目录 goroutine和channel概念开启线程与channel简单通信流程多个工作协程并发执行流程 goroutine和channel概念 goroutine(协程),一般我们常见的是进程,线程,进程可以理解为一个软件在运行执行的过程,线程跟协程比较类似&…...

亚组风险比分析与可视化

1. 结果解读 1.1 风险比概述 1.1.1 风险比基本概念 风险比(Hazard Ratio)用于衡量治疗组与对照组事件发生的风险差异。 风险比为1,表示两组风险相同;小于1,治疗组风险低;大于1,治疗组风险高。 1.1.2 性别亚组分析 A性风险比小于1,表明治疗对A性有积极效果,风险降低。…...

计算机网络核心知识点全解析(面试通关版)

一、网络体系结构:从OSI到TCP/IP的分层设计 1.1 七层模型与四层模型对比 OSI七层模型核心功能TCP/IP四层对应典型协议生活类比应用层为应用程序提供服务(如文件传输、邮件、Web浏览)应用层HTTP、FTP、SMTP、DNS快递面单信息(收件…...

使用 Frida 绕过 iOS 应用程序中的越狱检测

在这篇博文中,我们将介绍**Frida**,它是用于移动应用程序安全分析的真正有趣的工具之一。 我们在高级 Android 和 iOS 漏洞利用培训中也深入讲解了这一点,您可以在这里注册 -培训链接 即使您从未使用过 Frida,本文也将作为指南,帮助您进入 Frida 的世界,进行移动应用程…...

【博客系统】博客系统第一弹:博客系统项目配置、MyBatis-Plus 实现 Mapper 接口、处理项目公共模块:统一返回结果、统一异常处理

案例综合练习 - 博客系统 本节目标 从 0 到 1 完成博客系统后端项目的开发。 前言 通过前面课程的学习,我们掌握了 Spring 框架和 MyBatis 的基本使用,并完成了图书管理系统的常规功能开发。接下来我们系统地从 0 到 1 完成一个项目的开发。 项目介绍 …...

如何通过挖掘需求、SEO优化及流量变现成功出海?探索互联网产品的盈利之道

挖掘需求,优化流量,实现变现:互联网出海产品的成功之路 在当今全球化的数字时代,越来越多的企业和个人选择将业务扩展到国际市场。这一趋势不仅为企业带来了新的增长机会,也为个人提供了通过互联网产品实现盈利的途径…...

车载功能测试-车载域控/BCM控制器测试用例开发流程【用例导出方法+优先级划分原则】

目录 1 摘要2 位置灯手动控制简述2.1 位置灯手动控制需求简述2.2 位置灯手动控制逻辑交互图 3 用例导出方法以及优先级原则3.1 用例导出方法3.1.1 用例导出方法介绍3.1.2 用例导出方法关键差异分析 3.2 优先级规则3.2.1 优先级划分的核心原则3.2.2 具体等级定义与判定标准 3.3 …...

基于OpenMV+STM32+OLED与YOLOv11+PaddleOCR的嵌入式车牌识别系统开发笔记

基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记 基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记系统架构全景 一、实物演示二、OpenMV端设计要点1. 硬件配置优化2. 智能帧率控制算法3. 数据传输协议设计 三、PyTorch后端核心实现:YOLOv11与PaddleOCR的…...

MCP实战-本地MCP Server + Client实战

概述 本文开发一个MCP的Client和Server。然后通过本地模式来运行,并获取到server的结果。 MCP Server开发 import anyio import click import mcp.types as types from mcp.server.lowlevel import Server from pydantic import FileUrlSAMPLE_RESOURCES {"…...

w~嵌入式C语言~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/13870376 一、STM32怎么选型 什么是 STM32 STM32,从字面上来理解,ST是意法半导体,M是Microelectronics的缩写,32表示32位,合起来理解,STM32就是指S…...

lightrag : from lightrag.utils import EmbeddingFunc 报错

原因: 1. 同时安装了lightrag与lightrag-hku 解决方法: 卸载原有的lightrag与lightrag-hku,只安装lightrag-hku pip install lightrag-hku...

ppt流程图怎么?ppt流程图模板大全

ppt流程图怎么?ppt流程图剪头模板,ppt流程图模板大全: ppt流程图_模板素材_PPT模板_ppt素材_免抠图片_AiPPTer...

AWS中国区ICP备案全攻略:流程、注意事项与最佳实践

导语 在中国大陆地区开展互联网业务时,所有通过域名提供服务的网站和应用必须完成ICP备案(互联网内容提供商备案)。对于选择使用AWS中国区(北京/宁夏区域)资源的用户,备案流程因云服务商的特殊运营模式而有所不同。本文将详细解析AWS中国区备案的核心规则、操作步骤及避坑…...

腾讯云服务器安全——服务防火墙端口放行

点击服务进入安全策略 添加规则...

对局匹配--stl+模拟

1.模拟&#xff0c;先找匹配对数&#xff0c;然后减 2.特殊情况&#xff0c;k0 3.stl容器使用&#xff0c;lower_bound https://www.luogu.com.cn/problem/P8656 #include<bits/stdc.h> using namespace std; #define N 100011 typedef long long ll; typedef pair&…...

K8S安全认证

一。用户认证的基本框架 在K8S集群中&#xff0c;客户端通常有两类&#xff1a; 1.User Account&#xff1a;一般独立于K8S之外的其他服务管理的用过户账号 2.Service Account&#xff1a;K8S管理的账号&#xff0c;用于为Pod中的服务进程在访问K8S提供身份标识 ApiServer是…...

mybatis-plus里的com.baomidou.mybatisplus.core.override.MybatisMapperProxy 类的详细解析

以下是 com.baomidou.mybatisplus.core.override.MybatisMapperProxy 类的详细解析&#xff1a; 1. 类的作用 MybatisMapperProxy 是 MyBatis-Plus 框架中用于实现 Mapper 接口动态代理的核心类。它继承自 MyBatis 的 MapperProxy&#xff0c;并扩展了以下功能&#xff1a; …...

在java程序中,类,进程,线程他们之间的关系以及main方法与他们之间的关系

在 Java 程序里&#xff0c;类、进程、线程各自有着不同的概念&#xff0c;同时也存在着紧密的联系&#xff0c;下面来详细分析它们之间的关系以及 main 方法和它们的关系。 类 类是 Java 中最基础的编程单元&#xff0c;是一种自定义的数据类型&#xff0c;它定义了对象的属…...

[ACTF2020 新生赛]BackupFile题解

启动环境 进入后只有一段话&#xff0c;没有其他的说法。 解题方法 但是题目命名为backup file&#xff0c;应该是备份文件的意思&#xff0c;那么就用dirsearch工具来扫一下看看。 查看扫描结果 index.php.bak 下载下这个文件&#xff0c;查看文件内容。 进行php代码审计…...

如何修改npm的全局安装路径?

修改 npm 的全局安装路径可以通过以下步骤完成&#xff0c;确保全局包&#xff08;使用 -g 安装的模块&#xff09;和缓存文件存储到自定义路径。以下是详细步骤&#xff1a; 1. 创建自定义路径的目录 在目标路径下创建两个文件夹&#xff0c;分别用于存储全局模块和缓存文件…...

巧用 Element - UI 实现图片上传按钮的智能隐藏

引言 在前端开发中&#xff0c;使用 Element - UI 组件库来构建用户界面是非常常见的操作。其中图片上传功能更是在许多项目中频繁出现&#xff0c;比如用户头像上传、商品图片上传等场景。有时候&#xff0c;我们会有这样的需求&#xff1a;当上传图片达到一定数量后&#xf…...

Ubuntu18.04 升级最新版本Cmake

1.升级原因 ubuntu18.04默认安装的cmake版本为 3.10.2&#xff1b;在编译一些软件的时候&#xff0c;版本太低导致无法编译 2.从cmake官网下载二进制安装包 地址:https://cmake.org/download/ 包:cmake-4.0.1-linux-x86_64.tar.gz ​ 3.上传下载好的安装包到ubuntu&#xf…...

关于mysql的事务和索引

1. 事务四大特性&#xff08;ACID&#xff09; 原子性&#xff1a;事务的操作要么全部成功&#xff0c;要么全部失败回滚&#xff0c;不可分割。 一致性&#xff1a;事务执行前后&#xff0c;数据必须满足业务规则&#xff08;如账户总额不变&#xff09;。 隔离性&#xff1…...

从“拼凑”到“构建”:大语言模型系统设计指南!

你有没有试过在没有说明书的情况下组装宜家家具?那种手忙脚乱却又充满期待的感觉,和设计大语言模型(LLM)系统时如出一辙。如果没有一个清晰的计划,很容易陷入混乱。我曾经也一头扎进去,满心期待却又手足无措,被网上那些复杂的架构图搞得晕头转向。于是,我坐下来,把它们…...

mybatis xml中特殊字符处理

1&#xff0c;CDATA区&#xff1a; 它的全称为character data,以"<![CDATA[ "开始&#xff0c;以" ]]>" 结束&#xff0c;在两者之间嵌入不想被解析程序解析的原始数据&#xff0c;解析器不对CDATA区中的内容进行解析&#xff0c;而是将这些数据原封…...

【数据结构与算法】从完全二叉树到堆再到优先队列

完全二叉树 CBT 设二叉树的深度为 h , 若非最底层的其他各层的节点数都达到最大个数 , 最底层 h 的所有节点都连续集中在左侧的二叉树叫做 完全二叉树 . 特点 对任意节点 , 其右分支下的叶子节点的最底层为 L , 则其左分支下的叶子节点的最低层一定是 L 或 L 1 .完全二叉树…...

【linux】SSH 连接 WSL2 本地环境的完整步骤

SSH 连接 WSL2 本地环境的完整步骤 要在 Windows 的 WSL2 环境中启用 SSH 服务&#xff0c;并允许本地或局域网设备连接&#xff0c;需完成以下步骤&#xff1a; 1. 安装 openssh-server sudo apt update sudo apt install openssh-server -y2. 配置 sshd 修改配置文件 sud…...

vue项目前后端分离设计

在Vue前端架构中&#xff0c;通过分层结构和模块化设计实现高效的前后端分离&#xff0c;需要系统性规划各层职责、接口管理和数据流控制。以下是结合业界最佳实践的完整方案&#xff1a; 一、分层架构设计 1. 分层结构&#xff08;自上而下&#xff09; 层级职责示例技术实现…...

【Linux网络】构建类似XShell功能的TCP服务器

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...