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

Java并发编程:从synchronized到ReentrantLock与Condition的进阶实践

1. 为什么需要更强大的锁机制在Java多线程开发中synchronized关键字可能是大多数开发者最先接触的同步工具。记得我刚工作那会儿处理线程安全问题就是无脑加synchronized直到有次线上系统出现死锁排查了整整两天才发现问题所在。synchronized确实简单易用但在复杂的并发场景下就显得力不从心了。举个例子假设我们有个银行转账系统用synchronized实现大概长这样public class BankAccount { private double balance; public synchronized void transfer(BankAccount to, double amount) { this.balance - amount; to.balance amount; } }这种实现有个致命问题当A给B转账的同时B也给A转账就可能出现死锁。更麻烦的是一旦发生死锁synchronized既不能中断也不能设置超时系统就直接卡死了。这就是为什么我们需要更强大的锁机制——ReentrantLock。ReentrantLock提供了synchronized不具备的三大能力可中断锁等待锁的线程可以被中断超时机制可以设置获取锁的等待时间公平锁可以按申请锁的顺序获取锁在实际项目中我曾经用ReentrantLock重构过一个订单处理系统。原先用synchronized时高峰期经常出现线程堆积改用ReentrantLock的tryLock(500, TimeUnit.MILLISECONDS)后系统稳定性明显提升超时订单能够自动放弃处理而不是一直阻塞。2. ReentrantLock的核心特性解析2.1 基础锁功能对比先看一个最简单的计数器实现我们用三种方式分别实现// 不加锁版本 public class NonLockCounter { private int count; public void add() { count; // 非原子操作 } } // synchronized版本 public class SyncCounter { private int count; public synchronized void add() { count; } } // ReentrantLock版本 public class LockCounter { private int count; private final Lock lock new ReentrantLock(); public void add() { lock.lock(); try { count; } finally { lock.unlock(); } } }测试时开100个线程各执行100次add操作不加锁版本的结果每次都不一样而synchronized和ReentrantLock版本都能稳定输出10000。看起来效果一样但ReentrantLock的优势在于更灵活的控制。2.2 可中断与超时机制这是ReentrantLock最实用的特性之一。假设我们有个需要获取多个资源的操作public class ResourceManager { private final Lock lock1 new ReentrantLock(); private final Lock lock2 new ReentrantLock(); public boolean transferWithTimeout(long timeout, TimeUnit unit) throws InterruptedException { long stopTime System.nanoTime() unit.toNanos(timeout); // 尝试获取第一个锁 if (!lock1.tryLock(timeout, unit)) { return false; } try { // 计算剩余时间 long remaining stopTime - System.nanoTime(); // 尝试获取第二个锁 if (!lock2.tryLock(remaining, TimeUnit.NANOSECONDS)) { lock1.unlock(); // 记得释放已获取的锁 return false; } try { // 执行业务逻辑 return doTransfer(); } finally { lock2.unlock(); } } finally { lock1.unlock(); } } }这种写法完美避免了死锁风险我在实际项目中处理分布式锁时经常使用这种模式。相比之下synchronized一旦开始等待就只能死等没有任何回旋余地。2.3 公平锁与非公平锁ReentrantLock的另一个强大之处是可以创建公平锁Lock fairLock new ReentrantLock(true); // true表示公平锁公平锁会按照线程请求锁的顺序来分配锁避免了线程饥饿问题。不过要注意公平锁的性能通常比非公平锁低因为要维护请求队列。根据我的测试在高并发场景下非公平锁的吞吐量能比公平锁高出5-10倍。3. Condition的精准线程控制3.1 基本等待/通知机制Condition可以理解为ReentrantLock的等待/通知机制类似于Object的wait/notify但更灵活。先看个生产者消费者例子public class MessageQueue { private final Lock lock new ReentrantLock(); private final Condition notFull lock.newCondition(); private final Condition notEmpty lock.newCondition(); private final Object[] items new Object[100]; private int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count items.length) { notFull.await(); // 队列满时等待 } items[putptr] x; if (putptr items.length) putptr 0; count; notEmpty.signal(); // 通知消费者 } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count 0) { notEmpty.await(); // 队列空时等待 } Object x items[takeptr]; if (takeptr items.length) takeptr 0; count--; notFull.signal(); // 通知生产者 return x; } finally { lock.unlock(); } } }这个实现比用wait/notify更清晰而且可以创建多个Condition实现更精细的控制。我在一个日志处理系统中就用过这种模式不同的Condition分别处理不同优先级的日志。3.2 多Condition应用场景Condition最强大的地方在于一个Lock可以创建多个Condition。比如实现一个线程安全的连接池public class ConnectionPool { private final Lock lock new ReentrantLock(); private final Condition hasAvailable lock.newCondition(); private final Condition canReturn lock.newCondition(); private final int maxSize; private final SetConnection pool new HashSet(); private int activeCount 0; public Connection get() throws InterruptedException { lock.lock(); try { while (activeCount maxSize) { hasAvailable.await(); } Connection conn createConnection(); activeCount; canReturn.signal(); return conn; } finally { lock.unlock(); } } public void release(Connection conn) { lock.lock(); try { while (!pool.contains(conn)) { canReturn.await(); } pool.remove(conn); activeCount--; hasAvailable.signal(); } finally { lock.unlock(); } } }这种设计可以避免虚假唤醒问题而且不同状态的等待可以分开处理代码逻辑更清晰。4. 实战中的选择与优化4.1 何时选择ReentrantLock根据我的经验以下场景适合使用ReentrantLock需要可中断的锁获取操作需要超时控制的锁获取需要公平锁特性需要多个条件变量(Condition)需要尝试获取锁(tryLock)而简单场景下synchronized仍然是更好的选择因为语法更简洁JVM会优化synchronized的性能自动释放锁不会忘记解锁4.2 性能优化技巧在使用ReentrantLock时有几个性能优化点需要注意锁粒度尽量减小锁的代码块范围锁分离读写锁分离(ReentrantReadWriteLock)锁降级写锁降级为读锁避免锁嵌套容易导致死锁我曾经优化过一个商品库存系统将原来的大锁拆分为多个细粒度锁后QPS从200提升到了1500。关键代码片段public class Inventory { private final MapLong, Item items new HashMap(); private final MapLong, ReentrantLock locks new ConcurrentHashMap(); public void updateStock(long itemId, int delta) { ReentrantLock lock locks.computeIfAbsent(itemId, k - new ReentrantLock()); lock.lock(); try { Item item items.get(itemId); item.setStock(item.getStock() delta); } finally { lock.unlock(); } } }4.3 常见坑与解决方案在使用ReentrantLock时我踩过不少坑这里分享几个典型问题坑1忘记释放锁lock.lock(); try { // 业务代码 if(someCondition) { return; // 这里直接return会导致锁未释放 } } finally { lock.unlock(); // 必须放在finally块 }坑2锁重入次数不匹配public void methodA() { lock.lock(); try { methodB(); // 内部也调用了lock.lock() } finally { lock.unlock(); // 需要调用unlock()次数与lock()次数相同 } }坑3Condition使用不当Condition condition lock.newCondition(); // 错误必须先获取锁才能调用await() condition.await(); // 正确用法 lock.lock(); try { condition.await(); } finally { lock.unlock(); }这些坑我都真实踩过特别是第一个问题曾经导致我们线上系统出现严重的线程阻塞。后来我们制定了代码规范要求所有锁操作必须用try-finally包裹并在Code Review时重点检查。

相关文章:

Java并发编程:从synchronized到ReentrantLock与Condition的进阶实践

1. 为什么需要更强大的锁机制 在Java多线程开发中,synchronized关键字可能是大多数开发者最先接触的同步工具。记得我刚工作那会儿,处理线程安全问题就是无脑加synchronized,直到有次线上系统出现死锁,排查了整整两天才发现问题所…...

抖音批量下载终极神器:三分钟搞定无水印视频采集

抖音批量下载终极神器:三分钟搞定无水印视频采集 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖…...

别再只会用--help了!Python argparse的nargs和action参数实战避坑指南

深度解析Python argparse:掌握nargs与action参数的高阶用法 在Python开发中,命令行参数处理是构建可复用脚本的关键环节。虽然大多数开发者都能使用基础的add_argument方法,但当遇到需要处理复杂参数场景时——比如动态长度的输入列表、互斥的…...

别再死记硬背了!用这5个真实案例,彻底搞懂ABAP CDS里最让人头疼的语义注解(@Semantics)

别再死记硬背了!用这5个真实案例,彻底搞懂ABAP CDS里最让人头疼的语义注解(Semantics) 在ABAP CDS开发中,语义注解(Semantics)就像是一把双刃剑——用对了能让数据自解释,用错了则会…...

手机号逆向查询QQ号:3步快速实现的完整Python解决方案

手机号逆向查询QQ号:3步快速实现的完整Python解决方案 【免费下载链接】phone2qq 项目地址: https://gitcode.com/gh_mirrors/ph/phone2qq 在数字身份管理领域,手机号查QQ已成为众多开发者和企业用户的刚性需求。phone2qq项目提供了一个无需登录…...

从C语言到RISC-V汇编:手把手教你用汇编重写冒泡排序(附完整代码)

从C语言到RISC-V汇编:手把手教你用汇编重写冒泡排序(附完整代码) 在计算机科学的世界里,算法是灵魂,而汇编语言则是与硬件对话的最直接方式。今天,我们将一起踏上一段奇妙的旅程——用RISC-V汇编语言重新实…...

TVA算法轻量化在洗煤与光伏高节拍产线边缘侧的部署

前沿技术背景介绍:AI 智能体视觉检测系统(Transformer-based Vision Agent,缩写:TVA),是依托 Transformer 架构与“因式智能体”范式所构建的高精度智能体。它区别于传统机器视觉与早期 AI 视觉&#xff0c…...

嵌入式ADC避坑指南:I.MX6ULL采样不准?可能是这5个配置细节没做好(附校准与滤波代码)

I.MX6ULL ADC精度优化实战:从寄存器配置到滤波算法的完整解决方案 在嵌入式系统开发中,ADC(模数转换器)的精度问题就像一位难以捉摸的对手——当你以为已经掌握了所有技巧,它却总能在关键时刻给你"惊喜"。特…...

抖音评论采集终极指南:3步零代码实现自动化数据抓取

抖音评论采集终极指南:3步零代码实现自动化数据抓取 【免费下载链接】TikTokCommentScraper 项目地址: https://gitcode.com/gh_mirrors/ti/TikTokCommentScraper 还在为手动复制抖音评论而烦恼吗?想要批量获取视频评论数据却苦于没有技术背景&a…...

STM8S003F3P6串口通信避坑指南:为什么你的9600波特率总丢数据?

STM8S003F3P6串口通信避坑指南:为什么你的9600波特率总丢数据? 在嵌入式开发中,串口通信是最基础也最常用的功能之一。对于STM8S003F3P6这样的低成本MCU来说,串口通信看似简单,却隐藏着不少"坑"。很多开发者…...

AD7606多路采集时序翻车实录:从‘8+3路异常’到‘下降沿触发’的保姆级避坑指南

AD7606多路采集时序翻车实录:从‘83路异常’到‘下降沿触发’的保姆级避坑指南 当你在深夜的实验室里盯着示波器上那些跳动的波形,突然发现采集到的数据出现莫名其妙的错乱——前8路信号正常,后3路却像被施了魔法一样完全不对。这种场景对于使…...

从显性电平到隐性电平:一文搞懂CAN总线差分信号原理与TJA1050收发器实战配置

从显性电平到隐性电平:一文搞懂CAN总线差分信号原理与TJA1050收发器实战配置 当工程师第一次用示波器观察CAN总线信号时,往往会困惑:为什么两根线的电压差能传递信息?为什么显性电平可以"覆盖"隐性电平?这些…...

Ubuntu 18.04 + CUDA 9.0 环境下,保姆级避坑指南:从源码编译GCC 4.9.2到成功运行DensePose

Ubuntu 18.04 CUDA 9.0 环境下GCC 4.9.2源码编译全攻略:破解DensePose安装的核心难题 在计算机视觉领域,DensePose作为将2D图像中的人体像素映射到3D表面模型的重要工具,其安装过程却常常让开发者望而生畏。特别是在Ubuntu 18.04和CUDA 9.0环…...

告别纯点SLAM的尴尬:手把手教你用PL-SLAM+LSD在弱纹理环境跑通第一个Demo

从ORB-SLAM2到PL-SLAM:弱纹理环境下的实战突围指南 当你在空旷的白色走廊调试ORB-SLAM2时,是否经历过跟踪突然丢失的挫败感?这种场景下,墙面缺乏足够的纹理特征点,传统基于点特征的SLAM系统就像被蒙住双眼的探险者。这…...

i.MX6平台Linux下GOODIX GT657X触摸驱动移植保姆级教程(含设备树配置与常见问题排查)

i.MX6平台Linux下GOODIX GT657X触摸驱动移植实战指南 在嵌入式Linux开发中,触摸屏驱动的移植是连接硬件与用户体验的关键环节。本文将针对i.MX6处理器平台,详细解析GOODIX GT657X系列触摸芯片的驱动移植全过程,从设备树配置到内核编译&#x…...

N_m3u8DL-CLI-SimpleG:终极M3U8视频下载图形界面工具完整指南

N_m3u8DL-CLI-SimpleG:终极M3U8视频下载图形界面工具完整指南 【免费下载链接】N_m3u8DL-CLI-SimpleG N_m3u8DL-CLIs simple GUI 项目地址: https://gitcode.com/gh_mirrors/nm3/N_m3u8DL-CLI-SimpleG 在当今数字媒体时代,M3U8视频下载已成为许多…...

杰理之DMA名称修改【篇】

小度APP需要更新到最新版本才支持改设备名字...

避坑指南:Ubuntu20.04 高效部署 XTDrone 与 PX4 (v1.13) 仿真环境

1. 环境准备:系统与基础依赖 在Ubuntu 20.04上部署XTDrone与PX4仿真环境前,首先要确保系统环境干净。我遇到过不少因为残留旧版本组件导致的诡异问题,最稳妥的方式是使用新安装的系统。如果必须复用现有环境,建议先执行sudo apt a…...

DS4Windows终极指南:让PS手柄在PC上完美运行的5个秘密技巧

DS4Windows终极指南:让PS手柄在PC上完美运行的5个秘密技巧 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows 你是否曾经想过,为什么PS4/PS5手柄在PC上总是"水土…...

免费解锁QQ音乐加密文件:qmcdump终极使用完全指南

免费解锁QQ音乐加密文件:qmcdump终极使用完全指南 【免费下载链接】qmcdump 一个简单的QQ音乐解码(qmcflac/qmc0/qmc3 转 flac/mp3),仅为个人学习参考用。 项目地址: https://gitcode.com/gh_mirrors/qm/qmcdump 你是否曾经…...

企业微信智能客服源码首选 – 支持人工转接+知识库,立即获取

温馨提示:文末有资源获取方式在企业数字化转型的浪潮中,客户服务效率直接影响用户留存。今天分享一套基于PHP开发的智能客服系统源码,实测稳定,特别适合需要集成企业微信的团队。为什么推荐这套源码?1. 核心功能一览智…...

别再只看额定电流了!拆解一个日本进口漏电断路器,聊聊型号里那些容易忽略的关键参数

日本进口漏电断路器深度解析:隐藏在型号铭牌中的安全密码 当你从工具箱里取出那台日本进口的NV125-SV漏电断路器时,是否曾对着面板上密密麻麻的参数符号陷入沉思?在电气安全领域,每个字母和数字都是工程师精心设计的安全密码。今天…...

企业微信AI客服源码系统– 部署简单,维护方便,全程技术支持

温馨提示:文末有资源获取方式在当今数字化服务场景中,企业如何低成本实现724小时智能客户支持?一套稳定、易用的客服系统源码成为关键。以下基于实际开发经验,梳理该解决方案的核心优势:一、技术架构与部署优势PHP原生…...

别再折腾SDK了!用我改好的bundle.js,5分钟搞定网站Live2D看板娘(moc3格式)

5分钟极简部署:零配置玩转Live2D网页看板娘(moc3格式) 你是否曾在个人博客上看到那些会眨眼、转头的小看板娘,却苦于官方SDK的复杂配置而放弃?今天我要分享的这套方案,能让你跳过所有技术深坑——不需要懂…...

别再手动挂载了!CentOS 7.6服务器数据盘一键挂载与开机自启保姆级教程(含fdisk分区详解)

CentOS 7.6数据盘智能挂载全攻略:从分区到自启的零失误实践 刚接触Linux服务器运维时,最让人头疼的莫过于那块"看得见却用不了"的数据盘。每次重启后都要重新挂载的繁琐操作,不仅浪费时间,更可能因操作失误导致数据丢失…...

AssetRipper终极指南:5个技巧轻松提取Unity游戏资产

AssetRipper终极指南:5个技巧轻松提取Unity游戏资产 【免费下载链接】AssetRipper GUI Application to work with engine assets, asset bundles, and serialized files 项目地址: https://gitcode.com/GitHub_Trending/as/AssetRipper AssetRipper是一款功能…...

从贝塞尔函数到EMI:深入理解PWM谐波对电机噪音与电源干扰的影响

PWM谐波工程实战:从频谱分析到电机噪音与EMI治理 当伺服驱动器的啸叫声穿透车间背景噪音,或是变频器在EMC实验室频频触发测试警报时,工程师们面对的不仅是数学公式,更是产品可靠性的生死线。某医疗设备厂商曾因电机驱动系统的高频…...

Python量化分析新利器:3分钟掌握Mootdx通达信数据读取

Python量化分析新利器:3分钟掌握Mootdx通达信数据读取 【免费下载链接】mootdx 通达信数据读取的一个简便使用封装 项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx Mootdx是一个强大的Python开源库,专门用于读取通达信本地数据文件&am…...

EF Core 10向量搜索上线72小时后必须做的6项健康检查:从ANN精度衰减预警到HNSW图分裂检测(含Prometheus+Grafana监控模板)

第一章:EF Core 10向量搜索扩展的生产就绪性定义生产就绪性并非仅指功能可用,而是涵盖稳定性、可观测性、可维护性、安全边界与性能可预测性五个核心维度。EF Core 10 向量搜索扩展(Microsoft.EntityFrameworkCore.VectorSearch)虽…...

Proteus仿真翻车实录:ULN2003A驱动28BYJ-48步进电机,我的电路为什么总抖振不转?

Proteus仿真中ULN2003A驱动28BYJ-48步进电机的疑难排查指南 当你在Proteus中尝试用ULN2003A驱动28BYJ-48步进电机时,是否遇到过电机抖动但不旋转的情况?这种问题在仿真环境中尤为常见,往往由多个因素共同导致。本文将带你深入分析可能的原因&…...