新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java进阶篇之ReentrantLock:给等待设置期限,让取消及时生效

发布时间:2026/9/28 20:40:50来源:尧图网络
Java进阶篇之ReentrantLock:给等待设置期限,让取消及时生效
前一篇讨论了ConcurrentHashMap怎样协调共享数据。如果一次操作需要同时维护多个字段或者需要明确控制等待时间就要重新审视锁的使用方式。之前的《Java进阶篇之同步与锁》介绍过ReentrantLock的基本加锁与释放。今天继续往下走请求已经超时线程还应该排队吗任务被取消等待锁的线程能否及时离开没有拿到锁finally里还能调用unlock吗本文使用Java 21围绕等待、取消和释放展开不涉及分布式锁。一、先给等待选一种策略ReentrantLock提供了几种不同的获取方式。选择方法之前可以先问业务这次操作愿意等多久是否需要响应取消获取方式获取不到时等待期间能否通过中断退出lock()继续等待不会因中断而结束锁等待tryLock()立即返回false没有阻塞等待阶段tryLock(timeout, unit)最多等待指定时间超时返回false可以抛出InterruptedExceptionlockInterruptibly()等待获取可以抛出InterruptedException这里的“最多等待”指锁获取的超时语义线程调度可能让实际返回时刻稍晚它也不限制拿到锁后业务代码的执行时长。方法语义以Java 21的ReentrantLock文档为准。例如一个接口总预算为800ms锁获取已经消耗300ms那么后面的数据库操作不能再无条件获得完整800ms。调用链预算需要向下传递给每一步单独设置相同超时可能累加出更长的总耗时。二、超时与中断分别解决什么问题超时表达“我只愿意等到这里”。获取失败后可以返回繁忙、使用已有结果或者交给上层决定是否重试。中断表达外部发来的协作式取消信号。使用lockInterruptibly或带超时的tryLock时等待线程可以通过异常路径结束当前尝试。中断不会自动回滚已经完成的业务修改。图里的门代表临界区入口。计时器归零和收到取消信号都可能让等待者离开已经进入临界区的线程仍需负责自己的释放与收尾。异常处理也要明确职责能够继续向上抛出时保留InterruptedException在Runnable等不允许抛出受检异常的边界通常恢复中断标记并退出本次任务避免悄悄吞掉取消请求。三、完整示例验证超时和取消下面用主线程持有同一把锁分别启动两个工作线程。主线程在它们结束之前不释放锁因此结果不依赖“碰巧先运行哪一个线程”。CountDownLatch只用于确认第二个线程已经启动不负责制造锁竞争。保存为LockWaitingDemo.javaimportjava.util.concurrent.CountDownLatch;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.locks.ReentrantLock;publicclassLockWaitingDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{ReentrantLocklocknewReentrantLock();lock.lock();try{ThreadtimednewThread(()-{try{if(!lock.tryLock(100,TimeUnit.MILLISECONDS)){System.out.println(timed: timeout);return;}try{System.out.println(timed: acquired);}finally{lock.unlock();}}catch(InterruptedExceptione){Thread.currentThread().interrupt();System.out.println(timed: cancelled);}});timed.start();timed.join();CountDownLatchstartednewCountDownLatch(1);ThreadcancellablenewThread(()-{started.countDown();try{lock.lockInterruptibly();try{System.out.println(cancel: acquired);}finally{lock.unlock();}}catch(InterruptedExceptione){Thread.currentThread().interrupt();System.out.println(cancel: interruptedThread.currentThread().isInterrupted());}});cancellable.start();started.await();cancellable.interrupt();cancellable.join();}finally{lock.unlock();}System.out.println(main: lockedlock.isLocked());}}运行命令javac LockWaitingDemo.javajavaLockWaitingDemo输出timed: timeout cancel: interruptedtrue main: lockedfalse第一行来自等待超时。第二个线程可能在调用lockInterruptibly前就收到中断也可能已经开始等待这两种时序都会走取消路径。catch恢复标记后isInterrupted返回true。这里让主线程持锁并join工作线程是为了构造有限等待的演示条件。不要把第二个线程改为lock后照搬到业务它会等待主线程释放主线程又等待它结束从而互相卡住。四、finally必须对应一次成功获取最容易写错的是下面这种结构// 错误示意获取阶段抛出异常也会执行unlocktry{lock.lockInterruptibly();// 业务操作}finally{lock.unlock();}在尚未持有该锁的情况下被中断finally会尝试释放不属于当前线程的锁可能让IllegalMonitorStateException掩盖原来的取消原因。更清楚的结构是先成功获取再进入负责释放的trylock.lockInterruptibly();try{// 仅在拿到锁后执行}finally{lock.unlock();}带超时的版本同样先判断返回值。返回false的分支直接结束成功分支才配对unlock。这种写法把锁的所有权直接体现在代码结构里也符合Lock接口建议的释放模式。五、使用时再检查三个边界缩短持锁区间。把耗时日志、远程调用和可提前计算的内容移出临界区确实需要共同保护的状态则在锁内一起检查和修改。读取与修改的边界必须由业务不变量决定。取消后不要立即无限重试。如果上层已经取消任务重新排队只会让取消失去意义。超时重试也需要次数限制与剩余预算不能在循环里不断重置期限。重入也需要配对释放。同一线程可以再次获取ReentrantLock但每次成功获取都要对应一次unlock。为排查问题读取isLocked等状态可以辅助观察不应据此替代真正的获取操作观察结束后其他线程仍可能改变状态。六、 思维导图ReentrantLock等待控制获取策略立即尝试限时等待可中断等待退出原因超时返回false中断异常释放规则成功获取再进入tryfinally配对unlock业务边界传递剩余预算取消后结束任务七、总结总结要点等待策略要与业务预算一致。立即失败、限时等待和可中断等待分别适用于不同的调用需求。获取与释放应在结构上配对。先确认获取成功再进入finally负责的范围避免释放未持有的锁。取消处理需要贯穿调用链。中断到达之后线程要结束当前任务或向上交还处理权已有业务结果是否回滚需要另行设计。下一篇继续学习Condition线程怎样等待业务条件以及为什么await通常要放在while循环中。如果你觉得这篇文章对你有所帮助欢迎点赞、收藏、分享
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

AI视觉项目初始化:Windows目录结构与工具链实战指南 2026/9/28 21:28:42

AI视觉项目初始化:Windows目录结构与工具链实战指南

1. 这不是一条命令,而是一份AI视觉项目启动的“现场手记”你看到的这行mkdir D:\模块Bcd /d D:\模块Bmkdir 任务一成果 任务二成果 任务三成果 任务四成果,表面看是Windows命令行里一串混乱的mkdir指令,甚至带点语法错误——它根本跑不通。但…

阅读更多 →
S7协议通信实战:从握手到数据读写的深度解析 2026/9/28 21:28:42

S7协议通信实战:从握手到数据读写的深度解析

1. 工控现场为什么要死磕S7协议搞工控的兄弟大多有过这种经历:产线上位机要采一批西门子PLC的数据,拿了个现成的库,连上能读,但偶尔断、偶尔慢、偶尔读回来的浮点数明显不对。翻日志只看到一句“连接超时”,剩下的全靠…

阅读更多 →
手写数字识别系统Python课设:CNN模型训练与部署指南 2026/9/28 21:28:21

手写数字识别系统Python课设:CNN模型训练与部署指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
ARTEMIS 视觉驱动移动端自动化:从架构到实战的完整指南 2026/9/28 21:28:14

ARTEMIS 视觉驱动移动端自动化:从架构到实战的完整指南

移动端自动化这个方向,过去几年一直有个尴尬的瓶颈:脚本能点、能滑、能截图,但一旦界面稍有变化,整套流程就崩了。传统方案靠的是控件树和固定坐标,本质上是在"背答案",而不是"理解题目&quo…

阅读更多 →
FPGA以太网硬件设计:RTL8211F与RGMII接口实战避坑指南 2026/9/28 21:28:14

FPGA以太网硬件设计:RTL8211F与RGMII接口实战避坑指南

1. 为什么RTL8211F在FPGA以太网项目里出镜率这么高搞FPGA以太网通信的兄弟,大概率都绕不开RTL8211F这颗PHY芯片。我第一次用它是在一个图像采集项目里,FPGA端需要把采集到的数据实时传到上位机,千兆带宽是硬指标,选型的时候翻了一…

阅读更多 →
CUDA版本匹配原理:驱动、Toolkit与PyTorch/TensorFlow的ABI兼容性 2026/9/28 21:28:14

CUDA版本匹配原理:驱动、Toolkit与PyTorch/TensorFlow的ABI兼容性

1. 为什么CUDA版本不匹配会直接让PyTorch/TensorFlow“装死”——从GPU驱动到框架ABI的完整断层链你刚配好一台RTX 4060 Laptop GPU的笔记本,兴冲冲跑通了nvidia-smi,显卡状态绿油油,驱动版本显示535.104.05,一切看起来都对。可一…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉