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

【面试专栏|Java并发编程】ReentrantLock源码拆解:可重入+公平/非公平锁



🍃 予枫:个人主页

📚 个人专栏: 《Java 从入门到起飞》《读研码农的干货日常》《Java 面试刷题指南》
💻 Debug 这个世界,Return 更好的自己!

引言

在Java并发编程中,ReentrantLock是除synchronized外最常用的锁机制,它支持可重入、公平锁与非公平锁两种模式,灵活性远超synchronized。但很多开发者只停留在“会用”层面,对其底层原理、源码实现一知半解,面试时被追问就卡壳。本文将从源码出发,拆解ReentrantLock的可重入机制、公平锁与非公平锁的核心逻辑,搭配面试官高频追问,帮你吃透这一并发核心组件,面试、开发都能轻松应对~

文章目录

  • 引言
  • 一、REENTRANTLOCK 概述
  • 二、可重入原理(源码拆解)
    • 2.1 可重入的定义
    • 2.2 核心源码拆解(lock()方法)
      • 2.2.1 核心方法:tryAcquire(int arg)
      • 2.2.2 可重入逻辑解读
      • 2.2.3 unlock()方法(释放锁,配合可重入)
  • 三、公平锁与非公平锁(源码对比)
    • 3.1 构造方法(两种锁的初始化)
    • 3.2 核心差异:tryAcquire()方法
      • 3.2.1 非公平锁(NonfairSync)
      • 3.2.2 公平锁(FairSync)
      • 3.2.3 差异总结(表格对比)
    • 3.3 公平锁与非公平锁的获取流程(mermaid图)
  • 四、面试官追问环节(实战必备)
    • 追问1:ReentrantLock的可重入性,和synchronized的可重入性有什么区别?
    • 追问2:ReentrantLock为什么默认是非公平锁?
    • 追问3:ReentrantLock和synchronized的核心区别是什么?(高频必问)
    • 追问4:ReentrantLock的Condition是什么?怎么用?
  • 五、总结

一、REENTRANTLOCK 概述

ReentrantLock是JUC(java.util.concurrent)包下的可重入互斥锁,基于AQS(AbstractQueuedSynchronizer)实现,核心优势在于:

  • 支持可重入:同一线程可多次获取同一把锁,不会造成死锁;
  • 支持两种锁模式:公平锁(FairLock)和非公平锁(NonfairLock),可根据场景选择;
  • 灵活的锁操作:支持中断锁获取、超时获取锁,还能结合Condition实现精准通知/唤醒;
  • 性能优越:在高并发场景下,竞争激烈时性能优于synchronized(JDK1.8后两者性能差距缩小,但ReentrantLock灵活性依然突出)。

小贴士:ReentrantLock默认使用非公平锁,若需使用公平锁,可通过构造方法传入true:new ReentrantLock(true)。建议点赞收藏,后续开发、面试直接查阅~

二、可重入原理(源码拆解)

2.1 可重入的定义

可重入锁,通俗来说就是“同一线程可以多次获取同一把锁”,比如线程A获取锁后,再次调用需要该锁的方法时,无需释放锁即可直接获取,避免死锁。

ReentrantLock的可重入性,核心依赖AQS的状态变量(state)和当前持有锁的线程(exclusiveOwnerThread)实现,我们直接看源码。

2.2 核心源码拆解(lock()方法)

ReentrantLock的lock()方法会委托给内部类Sync(继承AQS)的lock()方法,而Sync有两个子类:FairSync(公平锁)和NonfairSync(非公平锁),但可重入逻辑是两者共有的,核心在acquire()和tryAcquire()方法。

2.2.1 核心方法:tryAcquire(int arg)

tryAcquire()是AQS的核心方法,用于尝试获取独占锁,ReentrantLock通过重写该方法实现可重入,源码如下(以非公平锁为例,公平锁逻辑类似,差异在竞争锁的顺序):

// NonfairSync的tryAcquire方法protectedfinalbooleantryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();intc=getState();// 获取AQS的状态变量state// 1. 若state为0,说明锁未被持有,尝试CAS获取锁if(c==0){if(compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);// 设置当前线程为锁的持有者returntrue;}}// 2. 若锁已被持有,判断持有锁的线程是否是当前线程(可重入核心逻辑)elseif(current==getExclusiveOwnerThread()){intnextc=c+acquires;// 锁重入次数+1(acquires默认是1)if(nextc<0)// 溢出判断,避免重入次数过多导致异常thrownewError("Maximum lock count exceeded");setState(nextc);// 更新state状态returntrue;}// 3. 锁已被其他线程持有,获取失败returnfalse;}

2.2.2 可重入逻辑解读

  1. state变量:AQS的state是一个volatile修饰的int变量,用于表示锁的持有状态:
    • state = 0:锁未被任何线程持有;
    • state > 0:锁被某一线程持有,state的值表示重入次数(每次重入+1,释放-1)。
  2. 可重入核心:当线程再次获取锁时,会判断当前持有锁的线程(exclusiveOwnerThread)是否是自己,若是,则直接将state+1,无需竞争锁,实现可重入。
  3. 锁释放:对应unlock()方法,每次释放会将state-1,直到state=0时,才会真正释放锁,让其他线程有机会获取。

2.2.3 unlock()方法(释放锁,配合可重入)

// ReentrantLock的unlock方法publicvoidunlock(){sync.release(1);}// AQS的release方法,委托给tryRelease()publicfinalbooleanrelease(intarg){if(tryRelease(arg)){Nodeh=head;if(h!=null&&h.waitStatus!=0)unparkSuccessor(h);returntrue;}returnfalse;}// ReentrantLock重写的tryRelease方法protectedfinalbooleantryRelease(intreleases){intc=getState()-releases;// 重入次数-1if(Thread.currentThread()!=getExclusiveOwnerThread())thrownewIllegalMonitorStateException();// 非持有锁线程释放锁,抛出异常booleanfree=false;if(c==0){// 重入次数为0,真正释放锁free=true;setExclusiveOwnerThread(null);// 清空持有锁的线程}setState(c);// 更新state状态returnfree;}

关键提醒:ReentrantLock的unlock()方法必须与lock()方法成对出现,重入多少次,就必须释放多少次,否则会导致锁无法释放,造成死锁!建议在finally块中释放锁,确保异常情况下也能正确释放。

三、公平锁与非公平锁(源码对比)

ReentrantLock的公平锁和非公平锁,核心差异在于“获取锁的顺序”:公平锁会按照线程排队的顺序获取锁(FIFO),非公平锁则允许线程“插队”获取锁,效率更高,但可能导致线程饥饿。

3.1 构造方法(两种锁的初始化)

// 无参构造:默认非公平锁publicReentrantLock(){sync=newNonfairSync();}// 有参构造:true=公平锁,false=非公平锁publicReentrantLock(booleanfair){sync=fair?newFairSync():newNonfairSync();}

3.2 核心差异:tryAcquire()方法

3.2.1 非公平锁(NonfairSync)

非公平锁的tryAcquire()方法,我们在2.2.1中已经看过,核心逻辑:

  • 锁未被持有(state=0)时,直接CAS尝试获取锁,不考虑队列中排队的线程;
  • 若锁已被持有,判断是否是当前线程,若是则重入。

简单说:非公平锁“不讲规矩”,只要锁空闲,不管队列里有没有线程排队,当前线程都能尝试抢锁,效率高,但可能导致排队久的线程一直抢不到锁(饥饿)。

3.2.2 公平锁(FairSync)

公平锁的tryAcquire()方法,在非公平锁的基础上,增加了“判断队列是否有等待线程”的逻辑:

// FairSync的tryAcquire方法protectedfinalbooleantryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();intc=getState();if(c==0){// 关键差异:增加hasQueuedPredecessors()判断,是否有线程在队列中等待if(!hasQueuedPredecessors()&&compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}elseif(current==getExclusiveOwnerThread()){intnextc=c+acquires;if(nextc<0)thrownewError("Maximum lock count exceeded");setState(nextc);returntrue;}returnfalse;}// 核心方法:判断队列中是否有比当前线程更早等待的线程publicfinalbooleanhasQueuedPredecessors(){Nodet=tail;// 队列尾节点Nodeh=head;// 队列头节点Nodes;// 队列不为空 && 头节点不是当前线程 && 头节点的后继节点不为空 && 后继节点的线程不是当前线程returnh!=t&&((s=h.next)==null||s.thread!=Thread.currentThread());}

3.2.3 差异总结(表格对比)

特性公平锁(FairSync)非公平锁(NonfairSync)
获取顺序按线程排队顺序(FIFO)允许插队,优先当前线程抢锁
效率较低(需判断队列,有上下文切换)较高(减少上下文切换)
线程饥饿不会出现(排队有序)可能出现(插队导致部分线程长期抢不到锁)
适用场景对公平性要求高、线程竞争不激烈的场景(如金融交易)高并发、对效率要求高的场景(如电商秒杀)

3.3 公平锁与非公平锁的获取流程(mermaid图)

state=0(未持有)

state>0(已持有)

成功

成功

执行完毕

state=0

state>0

线程调用lock

当前锁状态state

是否是公平锁

队列有等待线程?hasQueuedPredecessors

进入队列等待

CAS获取锁,state=1

当前线程是否是锁持有者?

state+1 重入

执行临界区代码

调用unlock

state-1

释放锁,唤醒队列线程

仅减少重入次数,不释放锁

四、面试官追问环节(实战必备)

面试中,面试官在问完ReentrantLock核心原理后,大概率会追问以下问题,提前备好应答,避免卡壳!

追问1:ReentrantLock的可重入性,和synchronized的可重入性有什么区别?

答:两者都支持可重入,但实现方式和底层逻辑不同:

  1. 实现方式:
    • ReentrantLock:基于AQS的state变量和exclusiveOwnerThread实现,重入时state+1,释放时state-1,手动控制锁的获取和释放;
    • synchronized:基于JVM底层监视器锁(monitor)实现,自动实现可重入(JVM会记录线程的重入次数),无需手动释放。
  2. 灵活性:ReentrantLock的可重入可配合中断、超时等机制,更灵活;synchronized的可重入是自动的,灵活性较差。

追问2:ReentrantLock为什么默认是非公平锁?

答:核心是“平衡效率和公平性”:

  • 非公平锁的效率更高:无需判断队列是否有等待线程,直接尝试抢锁,减少了线程上下文切换的开销(上下文切换会消耗CPU资源);
  • 实际场景中,公平性需求较低:大多数业务场景(如电商、接口调用)更看重并发效率,而非绝对的公平性;
  • 公平锁的弊端:即使锁空闲,线程也必须排队,会导致大量上下文切换,降低并发吞吐量。

追问3:ReentrantLock和synchronized的核心区别是什么?(高频必问)

答:核心区别体现在灵活性、性能、功能三个方面,总结如下:

维度ReentrantLocksynchronized
实现层面基于JDK(Java代码,AQS)基于JVM(底层C++实现)
锁类型可重入、公平/非公平锁(独占锁)可重入、非公平锁(独占锁)
锁操作手动lock()/unlock()(需finally释放)自动获取/释放(无需手动操作)
功能扩展支持中断、超时获取锁、Condition精准通知不支持中断、超时,通知方式单一(notify()/notifyAll())
性能高并发竞争激烈时,性能优于synchronized(JDK1.8后差距缩小)JDK1.8前性能较差,1.8后引入偏向锁、轻量级锁,性能提升明显

追问4:ReentrantLock的Condition是什么?怎么用?

答:Condition是ReentrantLock的配套工具,用于实现“精准通知/唤醒”,替代synchronized的wait()/notify(),核心优势是可以创建多个Condition对象,实现不同线程的分别通知。

用法示例:

ReentrantLocklock=newReentrantLock();Conditioncondition1=lock.newCondition();// 条件1Conditioncondition2=lock.newCondition();// 条件2// 线程A等待条件1lock.lock();try{while(条件不满足){condition1.await();// 等待,释放锁}// 执行逻辑}finally{lock.unlock();}// 线程B唤醒等待条件1的线程lock.lock();try{// 满足条件,唤醒线程Acondition1.signal();// 唤醒单个线程// condition1.signalAll(); // 唤醒所有等待的线程}finally{lock.unlock();}

五、总结

本文从源码出发,拆解了ReentrantLock的核心原理:

  1. 可重入性:基于AQS的state变量(重入次数)和exclusiveOwnerThread(锁持有者)实现,重入时state+1,释放时state-1;
  2. 公平锁与非公平锁:核心差异在tryAcquire()方法,公平锁需判断队列等待情况,非公平锁允许插队,效率更高;
  3. 实战价值:结合面试官高频追问,对比synchronized的差异、Condition的用法,帮你吃透面试重点。

ReentrantLock是Java并发编程的核心组件,掌握其底层源码和原理,不仅能应对面试,还能在实际开发中根据场景选择合适的锁策略,避免死锁、提升并发效率。

最后,觉得本文对你有帮助的话,点赞+收藏+关注哦!后续会持续拆解JUC核心组件,带你吃透并发编程,面试不踩坑~ 评论区说说你在使用ReentrantLock时遇到的问题吧!

http://www.cnnetsun.cn/news/1295022.html

相关文章:

  • 深度学习项目训练环境工业级鲁棒性:支持断网续训、磁盘满预警、OOM自动回滚
  • Gemma-3 Pixel Studio部署教程:4-bit量化降低显存占用至12GB实操步骤
  • 企业信息化系统的组成模块-支撑管理系统
  • 开源可部署!造相-Z-Image-Turbo LoRA Web服务镜像免配置快速上手
  • Janus-Pro-7B效果展示:高精度OCR识别+多轮视觉问答真实案例
  • UNIT-00:Berserk Interface构建AI编程助手:代码补全与解释
  • matplotlib中英文设置不同字体的方法
  • COLMAP实战:从无人机航拍照片到3D模型的完整流程(附避坑指南)
  • 2026 年,Flutter 已经可以在鸿蒙系统上跑起来了
  • wan2.1-vae多场景应用:海报设计/头像生成/教学配图一站式AI解决方案
  • Selenium的UI自动化测试屏幕截图功能实例代码
  • S32K144实战:基于SDK的Bootloader与APP无缝跳转设计
  • 避坑指南:SHAP的summary_plot修改颜色不生效?可能是cmap参数没用对
  • 手机检测WebUI权限管理:基于OAuth2的多角色(管理员/监考员)控制
  • 立创开源:基于中科蓝汛AB5301A与启英泰伦CI1302的离线语音蓝牙音箱全方案解析
  • DeepSeek-R1 1.5B完全指南:下载、部署、使用、优化一步到位
  • Cesium模型与视频结合实战:提升三维场景动态展示效率的解决方案
  • 4大优势!FastAPI Admin让后台开发效率提升80%
  • YimMenu全面指南:从入门到精通的开源GTA V辅助工具
  • 我写了一本从零实现深度学习与大语言模型的入门教程
  • 从行程编码到形态学:Halcon中erosion、connection、fill_up的算法实现与优化
  • 【第二周】RAG与Agent实战07:提示词优化案例_金融信息抽取
  • 0314爬紫金山
  • ChatGPT Python SDK深度实战:从API封装到生产环境最佳实践
  • Android应用集成AI:将Nanbeige 4.1-3B模型API封装为移动端SDK
  • 51单片机时钟电路设计避坑指南:从晶振选型到PCB布局的5个关键细节
  • wps word 修改无格式粘贴快捷键为 ctrl+shift+v
  • 如何突破SIM卡区域限制?3大创新技术重构跨境网络体验
  • MPh颠覆式仿真自动化:全流程工程问题的Python解决方案
  • 逆向解析百度搜索核心技术