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

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:适合无锁操作且对性能要求极高的场景,如计数器更新。
  • 使用锁:适用于有多个线程访问共享资源时,需要保证原子性和数据一致性,且数据量较大时使用锁更安全。

🙏 感谢阅读!

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

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


相关文章:

  • JVM常见的垃圾回收器
  • PyDantic入门介绍:Python数据验证与解析的利器
  • WPF之XAML基础
  • Java + Spring Boot + MyBatis获取以及持久化sql语句的方法
  • Redux 容器 | 原理解析
  • shell编程基础知识及脚本示例
  • 设计模式每日硬核训练 Day 16:责任链模式(Chain of Responsibility Pattern)完整讲解与实战应用
  • 分析型数据库入门指南:如何选择适合你的实时分析工具?
  • 哈希表基础
  • Ollama 在本地分析文件夹中的文件
  • 本安型交换机 + TSN:煤矿智能化的关键拼图
  • AI大模型从0到1记录学习 linux day21
  • 【论文阅读】-周总结-第5周
  • IDEA中使用Git
  • Vue2、Vue3区别之响应式原理
  • 深入理解 Java 单例模式:从基础到最佳实践
  • 【项目篇之垃圾回收】仿照RabbitMQ模拟实现消息队列
  • 查回来的数据除了 id,其他字段都是 null
  • 自然语言处理之机器翻译:注意力机制在低资源翻译中的突破与哲思
  • LeetCode每日一题4.27
  • 女乘客遭顺风车甩客、深夜丢高速服务区,滴滴霸道回应:赔五百元
  • 银川市长信箱被指“已读乱回”,官方通报:对相关责任人问责处理
  • 上海市政府常务会议研究抓好稳就业稳企业稳市场稳预期工作,让企业感受温度
  • 这些被低估的降血压运动,每天几分钟就管用
  • 5月动漫|“爱死机”即将回归,《明末》或是下一个大IP?
  • 别让心脏“饿肚子”,心肌缺血全解析