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

破茧成蝶:Java后端从0到资深工程师的进阶之路(五)

破茧成蝶Java后端从0到资深工程师的进阶之路五并发篇——多线程与高并发实战现代后端系统高并发是绕不开的挑战。多线程编程就像一把双刃剑用得好了系统吞吐量飙升用得不好死锁、内存泄漏、性能骤降接踵而至。本篇将带你深入 Java 并发核心从CompletableFuture异步编排到线程池调优再到并发容器的底层原理助你成为真正的并发编程高手。写在前面很多开发者提到并发第一反应是synchronized和volatile但对CompletableFuture的异步编排、ThreadLocal的内存泄漏、线程池的动态调优、ConcurrentHashMap的扩容机制等缺乏深入理解。结果就是线上突然出现 OOM、CPU 飙升、线程阻塞却不知道从何下手。一个资深开发者眼中的并发异步编程能熟练使用CompletableFuture编排任务依赖合理设置超时与异常处理。线程池能根据业务场景选择合适的线程池类型并实现参数的动态调整。并发容器能深入理解ConcurrentHashMap的扩容机制根据场景选择读写锁或分段锁避免锁竞争。本篇文章我们将从这些核心点出发结合实战案例帮你构建稳固的并发知识体系。一、JUC 核心类库的深度解析1.1CompletableFuture实现异步编排任务间的依赖、超时控制、异常处理CompletableFuture是 Java 8 引入的异步编程利器它将回调、任务编排、异常处理融为一体让异步代码不再陷入“回调地狱”。1.1.1 任务间的依赖编排场景查询用户信息、订单信息、商品信息三者并行执行最后汇总结果。用户信息与订单信息无依赖可并行。商品信息依赖订单信息从订单中获取商品 ID。代码实现publicCompletableFutureUserDTOqueryUser(LonguserId){returnCompletableFuture.supplyAsync(()-userService.getById(userId));}publicCompletableFutureOrderDTOqueryOrder(LongorderId){returnCompletableFuture.supplyAsync(()-orderService.getById(orderId));}publicCompletableFutureProductDTOqueryProductByOrder(OrderDTOorder){returnCompletableFuture.supplyAsync(()-productService.getById(order.getProductId()));}// 编排publicCompletableFutureResultDTOassembleResult(LonguserId,LongorderId){CompletableFutureUserDTOuserFuturequeryUser(userId);CompletableFutureOrderDTOorderFuturequeryOrder(orderId);// 等订单查询完成后根据订单查询商品依赖CompletableFutureProductDTOproductFutureorderFuture.thenCompose(this::queryProductByOrder);// 三个任务全部完成后合并结果returnCompletableFuture.allOf(userFuture,orderFuture,productFuture).thenApply(v-{ResultDTOresultnewResultDTO();result.setUser(userFuture.join());result.setOrder(orderFuture.join());result.setProduct(productFuture.join());returnresult;});}常用方法thenApply转换结果同步。thenCompose链式组合返回新的 CompletableFuture。thenCombine两个任务并行合并结果。allOf等待所有任务完成。anyOf任一任务完成即返回。1.1.2 超时控制避免任务无限阻塞CompletableFuture提供了orTimeout和completeOnTimeout方法Java 9。CompletableFutureStringfutureCompletableFuture.supplyAsync(()-{// 模拟耗时操作Thread.sleep(5000);returnresult;}).orTimeout(3,TimeUnit.SECONDS).exceptionally(ex-timeout);// 超时后返回默认值1.1.3 异常处理CompletableFuture中的异常不会直接抛出而是通过exceptionally或handle捕获。CompletableFutureStringfutureCompletableFuture.supplyAsync(()-{if(true)thrownewRuntimeException(业务异常);returnsuccess;}).exceptionally(ex-{log.error(执行失败,ex);returnfallback;});资深提示CompletableFuture默认使用ForkJoinPool.commonPool()其线程数是 CPU 核心数 - 1。对于 IO 密集型任务应自定义线程池避免与计算密集型任务争抢。1.2ThreadLocal的内存泄漏分析与实战InheritableThreadLocal 的缺陷与阿里 TransmittableThreadLocal 的解决方案1.2.1ThreadLocal的内存泄漏原理ThreadLocal内部使用ThreadLocalMap存储数据其 Key 是ThreadLocal对象的弱引用。当ThreadLocal对象不再被强引用时下一次 GC 会被回收但 Value 仍然是强引用且存在于 Thread 的ThreadLocalMap中。如果线程一直存活如线程池中的线程那么这些 Value 永远不会被回收导致内存泄漏。典型泄漏场景使用线程池但在任务结束后没有调用remove()清除 ThreadLocal 值。正确用法try{threadLocal.set(value);// 执行业务}finally{threadLocal.remove();// 务必移除}1.2.2InheritableThreadLocal的缺陷InheritableThreadLocal允许子线程继承父线程的变量值但在线程池场景下存在严重问题线程池复用了线程子线程的变量值可能被复用导致数据错乱。1.2.3 阿里TransmittableThreadLocal解决方案TransmittableThreadLocal是阿里开源的组件专门解决线程池场景下上下文传递的问题。它通过TtlRunnable或TtlExecutorService包装任务在任务执行前将父线程的上下文复制到当前线程执行后清除实现安全传递。引入依赖dependencygroupIdcom.alibaba/groupIdartifactIdtransmittable-thread-local/artifactIdversion2.14.2/version/dependency使用示例// 定义 TransmittableThreadLocalTransmittableThreadLocalStringcontextnewTransmittableThreadLocal();// 设置值context.set(userId);// 包装线程池ExecutorServiceexecutorTtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(10));// 提交任务时自动复制上下文executor.submit(()-{System.out.println(context.get());// 能正确获取父线程的 userId});// 最后清理可选context.remove();资深提示在生产环境中使用线程池且需要传递上下文如链路追踪 traceId、用户信息时强烈推荐使用TransmittableThreadLocal避免手动传递的繁琐和隐患。二、线程池的“避坑指南”2.1Executors工厂类创建的常见陷阱OOM 风险Executors提供了快速创建线程池的方法但隐藏着致命缺陷方法队列风险newFixedThreadPoolLinkedBlockingQueue(无界队列)任务堆积可能导致 OOMnewCachedThreadPoolSynchronousQueue(无容量)线程数无上限可能创建过多线程导致系统崩溃newSingleThreadExecutorLinkedBlockingQueue(无界队列)同 fixed任务堆积 OOMnewScheduledThreadPoolDelayedWorkQueue(无界队列)同 fixed任务堆积 OOM案例某电商促销期间使用newFixedThreadPool处理订单异步任务高峰期任务堆积数千万最终导致内存溢出整个服务宕机。正确做法手动创建ThreadPoolExecutor明确设置队列大小和拒绝策略。intcorePoolSizeRuntime.getRuntime().availableProcessors();intmaxPoolSizecorePoolSize*2;longkeepAliveTime60L;BlockingQueueRunnableworkQueuenewArrayBlockingQueue(2000);RejectedExecutionHandlerhandlernewThreadPoolExecutor.CallerRunsPolicy();ThreadPoolExecutorexecutornewThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,TimeUnit.SECONDS,workQueue,Executors.defaultThreadFactory(),handler);拒绝策略选择AbortPolicy直接抛异常默认适合关键任务需要上游重试。CallerRunsPolicy由调用线程执行适合非关键任务可降级。DiscardPolicy直接丢弃不通知慎用。DiscardOldestPolicy丢弃队列中最老的任务适合实时性要求高的场景。2.2 如何动态调整线程池参数美团线程池动态调优方案的简易实现生产环境中业务流量是动态变化的静态配置的线程池参数往往无法完美适应。美团技术团队提出的动态线程池思路值得借鉴通过配置中心如 Apollo、Nacos动态修改核心参数并实时生效。2.2.1 实现思路封装一个DynamicThreadPool持有ThreadPoolExecutor实例。提供方法更新核心线程数、最大线程数、队列容量等参数。监听配置变化调用setCorePoolSize、setMaximumPoolSize等方法。注意setCorePoolSize和setMaximumPoolSize的规则如果新值大于旧值会立即创建新线程如果小于旧值会在空闲时回收。2.2.2 简易实现ComponentpublicclassDynamicThreadPool{privatefinalThreadPoolExecutorexecutor;publicDynamicThreadPool(){this.executornewThreadPoolExecutor(10,20,60,TimeUnit.SECONDS,newArrayBlockingQueue(1000),newThreadPoolExecutor.CallerRunsPolicy());}publicvoidupdateCorePoolSize(intcorePoolSize){executor.setCorePoolSize(corePoolSize);}publicvoidupdateMaxPoolSize(intmaxPoolSize){executor.setMaximumPoolSize(maxPoolSize);}publicvoidupdateQueueCapacity(intcapacity){BlockingQueueRunnablenewQueuenewArrayBlockingQueue(capacity);// 注意直接替换队列会丢失原有任务需要谨慎实现// 更安全的方式是使用 ResizableCapacityLinkedBlockingQueue可修改容量的队列}// 提交任务的方法...}2.2.3 配合配置中心以 Nacos 为例RefreshScopeComponentpublicclassThreadPoolConfig{Value(${threadpool.coreSize:10})privateintcoreSize;Value(${threadpool.maxSize:20})privateintmaxSize;AutowiredprivateDynamicThreadPooldynamicThreadPool;PostConstructpublicvoidinit(){dynamicThreadPool.updateCorePoolSize(coreSize);dynamicThreadPool.updateMaxPoolSize(maxSize);}EventListenerpublicvoidonRefresh(RefreshScopeRefreshedEventevent){// 配置刷新后重新设置dynamicThreadPool.updateCorePoolSize(coreSize);dynamicThreadPool.updateMaxPoolSize(maxSize);}}资深提示动态调整线程池参数时需要关注corePoolSize和maximumPoolSize的调整策略避免频繁创建销毁线程。同时队列容量的动态替换需要格外小心推荐使用可扩容的队列实现如自定义ResizableCapacityLinkedBlockingQueue。三、并发容器与锁优化3.1ConcurrentHashMap的size方法原理ConcurrentHashMap的size()方法并不像普通 HashMap 那样直接返回一个字段而是通过一种近似统计的方式实现以平衡并发与准确性。JDK 1.8 的实现维护一个baseCount和CounterCell数组类似分段计数。当多个线程同时更新时每个线程会随机选择CounterCell进行累加减少对baseCount的竞争。size()方法会累加baseCount和所有CounterCell的值但这个过程不加锁因此得到的 size 是弱一致性的可能不是最新值。源码片段JDK 1.8publicintsize(){longnsumCount();return((n0L)?0:(n(long)Integer.MAX_VALUE)?Integer.MAX_VALUE:(int)n);}finallongsumCount(){CounterCell[]ascounterCells;CounterCella;longsumbaseCount;if(as!null){for(inti0;ias.length;i){if((aas[i])!null)suma.value;}}returnsum;}结论ConcurrentHashMap的size()方法并不是精确的在高并发场景下可能返回一个旧值。如果需要精确计数应使用LongAdder或维护独立计数器。3.2 分段锁、读写锁在实际业务场景下的性能对比3.2.1 分段锁JDK 1.7 ConcurrentHashMapJDK 1.7 的ConcurrentHashMap采用分段锁Segment默认 16 个 Segment每个 Segment 独立加锁将锁粒度从整个表缩小到单个 Segment从而提升并发度。适用场景读写比例均衡但 JDK 1.8 已放弃分段锁改用 CAS synchronized 优化性能更好。3.2.2 读写锁ReentrantReadWriteLockReentrantReadWriteLock允许多个读线程并发写线程互斥。适合读多写少的场景。示例缓存实现publicclassCacheK,V{privatefinalMapK,VcachenewHashMap();privatefinalReadWriteLocklocknewReentrantReadWriteLock();privatefinalLockreadLocklock.readLock();privatefinalLockwriteLocklock.writeLock();publicVget(Kkey){readLock.lock();try{returncache.get(key);}finally{readLock.unlock();}}publicvoidput(Kkey,Vvalue){writeLock.lock();try{cache.put(key,value);}finally{writeLock.unlock();}}}性能对比读多写少读写锁 普通锁synchronized 分段锁如果分段数较少。写多分段锁或ConcurrentHashMap可能更优因为读写锁的写操作会阻塞所有读操作。Java 8StampedLock提供了乐观读性能比ReentrantReadWriteLock更高但使用更复杂。3.2.3 业务选择建议场景推荐工具原因缓存读远多于写ReentrantReadWriteLock或StampedLock读并发高写锁影响小高频读写如计数器LongAdder分段思想无锁性能极高通用并发 MapConcurrentHashMap综合性能最好无需额外锁需要精确控制锁粒度分段锁自定义可基于 key 分段减少竞争资深提示不要盲目使用读写锁。如果读操作非常快例如内存读取使用读写锁带来的上下文切换开销可能超过锁竞争的开销此时普通锁可能更快。性能调优应基于压测数据。总结本篇我们从并发编程的三个关键领域深入JUC 核心类库CompletableFuture实现异步任务编排、超时与异常处理提升系统吞吐量。ThreadLocal内存泄漏分析与TransmittableThreadLocal在线程池场景下的安全传递。线程池避坑避免使用Executors创建无界队列或无限线程的线程池。实现动态线程池参数调整应对流量变化。并发容器与锁优化理解ConcurrentHashMap的size()方法弱一致性。根据业务场景选择读写锁、分段锁或无锁结构追求极致性能。并发编程是 Java 后端进阶的必经之路理解这些原理和实战技巧你将能在高并发场景下写出更稳定、更高效的代码。下篇预告《中间件篇——消息队列与数据缓存的博弈》将带你深入 Redis 缓存三大坑、消息队列的可靠性保证以及分布式事务的最终一致性方案敬请期待如果觉得本文对你有帮助欢迎点赞、收藏、评论你的支持是我持续创作的动力

相关文章:

破茧成蝶:Java后端从0到资深工程师的进阶之路(五)

破茧成蝶:Java后端从0到资深工程师的进阶之路(五)并发篇——多线程与高并发实战现代后端系统,高并发是绕不开的挑战。多线程编程就像一把双刃剑:用得好了,系统吞吐量飙升;用得不好,死…...

带你读顶会论文丨基于溯源图的APT攻击检测

带你读顶会论文丨基于溯源图的APT攻击检测 **摘要:**本次分享主要是作者对APT攻击部分顶会论文阅读的阶段性总结,将从四个方面开展。 本文分享自华为云社区《[论文阅读] (10)基于溯源图的APT攻击检测安全顶会总结》,作者:eastmoun…...

Xray-强大的漏洞扫描工具

Xray-强大的漏洞扫描工具 Xray是什么? xray (https://github.com/chaitin/xray) 是从长亭洞鉴核心引擎中提取出的社区版漏洞扫描神器,支持主动、被动多种扫描方式,自备盲打平台、可以灵活定义 POC,功能丰富,调用简单&a…...

2026年,探秘义乌一次性包装盒定做厂家的独特工艺与优质服务!

在商品包装需求日益多样化的今天,一次性包装盒的定制市场愈发繁荣。义乌,作为全球知名的小商品之都,拥有众多一次性包装盒定做厂家,它们以独特的工艺和优质的服务在市场中占据一席之地。今天,我们将走进一家具有代表性…...

CMMI 能力成熟度模型集成介绍

CMMI(Capability Maturity Model Integration)即能力成熟度模型集成,是由美国卡内基梅隆大学软件工程研究所(SEI)研发、现由ISACA旗下CMMI 研究院维护的国际权威过程改进与评估框架,核心是通过标准化最佳实…...

水厂供水泵房自控案例(工程实际在用) PLC程序+触摸屏程序+组态软件程序+图纸

水厂供水泵房自控案例(工程实际在用) PLC程序触摸屏程序组态软件程序图纸;凌晨三点,水厂中控室的警报突然炸响。我盯着屏幕上跳动的压力曲线,右手已经摸到了对讲机——这种情况在供水泵房太常见了。今天要聊的自控系统,可是我们项…...

2026年深圳冷冻食品包装盒代理,其中商机你知道多少?

在深圳这个充满活力与机遇的城市,冷冻食品市场一直呈现出稳步增长的态势。随着消费者对冷冻食品需求的不断增加,冷冻食品包装盒的市场需求也随之水涨船高。2026 年,深圳冷冻食品包装盒代理蕴含着巨大的商机。下面就为你详细剖析其中的商机以及…...

基于YOLOv10深度学习的植物叶片病害识别检测系统(YOLOv10+YOLO数据集+UI界面+Python项目+模型)

一、项目介绍 本项目基于先进的YOLOv10目标检测算法,开发了一套智能植物叶片病害识别检测系统。系统能够实现对38种不同植物叶片健康状况的实时检测与分类,包括多种常见病害及健康叶片。通过图形用户界面,用户可以方便地上传图片、视频或调用…...

基于MATLAB平台PCA的人脸识别:开启识别新征程

基于MATLAB平台PCA的人脸识别,程序已调通,可将自己的数据替换进行识别。 得到识别准确率结果。最近在研究人脸识别技术,基于MATLAB平台利用PCA(主成分分析)实现了一个人脸识别程序,现在跟大家分享分享。 PC…...

10:2026 AI变现实战总览:内容、工具、信息差三种变现闭环

作者: HOS(安全风信子) 日期: 2026-04-01 主要来源平台: GitHub 摘要: 提前剧透12大模块如何串联成3条可复制的赚钱路径。本文构建内容变现2.0闭环全图(Agentic生成)、工具/SaaS变现闭环全图(Ag…...

Artemis II宇航员在太空中遭遇Outlook故障问题

许多沮丧的用户都曾发誓要把微软Outlook发射到太空中,但NASA实际上已经这样做了——在一次绕月之旅中,现在它正给宇航员带来麻烦。目前正在环绕地球的猎户座飞船上的宇航员正在处理一系列日常维护任务,包括让他们的设备正常工作。从与休斯顿控…...

【芯片后仿(Post-Silicon Simulation)完全指南:从入门到流片前的最后一道防线】

一、什么是后仿?为什么要做后仿?后仿,全称Post Netlist Simulation(Post-Sim)或Gate Level Simulation(GLS),是指在RTL代码综合成门级网表后,通过反标SDF(Sta…...

QuiX公司取得光子量子计算纠错重大突破

QuiX Quantum公司周四宣布,该公司已成功演示了光子量子计算机中首个低于阈值的错误缓解技术,这一突破被认为有助于实现可扩展的容错量子系统。QuiX表示,其方法将物理量子比特的错误率降低到与大规模量子计算兼容的水平。这些研究结果是在QuiX…...

谷歌发布Gemma 4模型,为低功耗设备带来复杂推理能力

谷歌发布了其最先进的开放权重人工智能模型家族Gemma 4,这标志着开放权重AI模型领域的重大进步。技术架构与性能突破Gemma 4基于与Gemini 3相同的架构基础构建,专门设计用于处理复杂推理任务,并支持在工作站和智能手机等低功耗设备上本地运行…...

企业AI应用开发:从零构建企业级AI智能体的全流程指南

一文讲透智能体开发的核心要素,让AI真正融入业务系统随着大模型技术的成熟,AI智能体正从概念走向企业核心业务。对于信息中心和软件开发团队而言,如何低成本、高效率地将AI能力嵌入业务流程,已成为技术选型的核心考量。本文将系统…...

从“被看错”到“卖爆”,宇树机器人全国首店开业:机器狗平价上路,人形机器人掀价!

在经历了八年的深耕与资本的反复试炼后,杭州宇树科技(Ushush Technology)今天迎来了一个标志性的时刻——其全国首家线下门店正式在京东Mall西门入口处开业。这不仅是宇树向消费者展示“具身智能”实力的窗口,更是其“讲人形的故事…...

2026 Java AI框架选型:Spring AI/LangChain4j企业级对比

文章目录引子:Java程序员的"AI焦虑"一、血统与基因:两个截然不同的"家族遗传"1.1 Spring AI:Spring生态的"嫡长子"1.2 LangChain4j:Java AI界的"瑞士军刀"二、代码实战:同样的…...

MPC轨迹跟踪:基于运动学、动力学CarsimSimulink联仿

(MPC)轨迹跟踪,基于运动学、动力学carsim&simulink联仿方向打死油门踩到底,轮胎和地面摩擦的青烟还没散尽,手里的MPC控制器已经算好了未来三秒的轨迹——这大概就是模型预测控制在轨迹跟踪中最性感的瞬间。今天咱们…...

【从零开始学Java | 第二十五篇】TreeSet

目录 前言 一、TreeSet的特点 二、TreeSet集合默认的规则 1.默认排序/自然排序 2.比较器排序 总结 前言 在 Java 的集合框架中,Set 接口代表了一个不允许存在重复元素的集合。我们最常用的通常是 HashSet,因为它提供了极高的查找和插入效率。但是&…...

openclaw源码

https://github.com/openclaw/openclaw https://github.com/VoltAgent/awesome-openclaw-skills/tree/main/categories...

优峰技术 1550nm 可调谐激光器:全光纤型分支器件检测核心光源

全光纤型分支器件是光纤通信、光纤传感网络的核心无源元件,其插入损耗、回波损耗、偏振相关损耗、分光比均匀性等关键指标,直接决定光网络传输质量与稳定性。在全光纤型分支器件检测体系中,1550nm可调谐激光器作为高精度测试光源,…...

魔方财务批量拉取产品信息教程

使用魔方财务,有时候经常上级【变化了ip】或者批量【补时间】什么的,我们这里因为我们的财务换过域名,导致上级无法给我们推送需要我们手动拉取信息,一个两个还好,几百个怎么办? 本教程就是【欧云服务器】…...

驾校招生断崖式下跌?这3个数据驱动的获客策略,让报名量翻倍

驾校招生断崖式下跌?这3个数据驱动的获客策略,让报名量翻倍最近和几位驾校校长聊天,听到最多的感慨是:“以前学员排队等车,现在教练排队等学员。”这不是个别现象。某地驾培协会数据显示,2023年区域性驾校平…...

基于单片机的婴儿看护系统设计

一、摘要 本课论文构思并实现了一种基于STM32F103C8T6单片机的智能婴儿看护系统婴儿看护系统,该系统致力于为婴儿提供全方位的监测与智能婴儿看护系统化的照护服务。它巧妙地融合了DHT11温湿度传感器、声音传感器以及液体传感器,这些传感器协同工作&…...

​Problem - 2148F - Codeforces​[字符串后缀排序]

Problem - 2148F - Codeforces 题意很简单 我们可以随意防止字符串 按照从上到下 如果最后一层某个位置没有字符串 那么上面的字符串就会掉下来到最后一层 求字典序最小的最下层的字符串 首先 最朴素的思想 我们会找出当前最小长度的字符串 长度k 然后截取所有字符串的…...

国产SeekWave 双频WIFI6+BT5.4 VS6621SR80基于RK3588平台成功替换RTL8822模组 硬件兼容 速率可达600Mbps

RK3588是瑞芯微(Rockchip)推出的旗舰级SoC芯片,采用8nm工艺,集成‌四核Cortex-A76和四核Cortex-A55 CPU、ARM Mali-G610 MP4 GPU、6 TOPS NPU‌,支持8K视频编解码。‌‌1‌‌2‌CPU‌:八核ARM架构&#xff…...

解锁论文写作新境界:书匠策AI——学术探索的智能导航灯

在学术的浩瀚海洋中,每一位研究者、学生乃至教育博主,都如同勇敢的航海家,驾驶着知识的船只,追寻着真理的彼岸。然而,论文写作这一航程中的关键环节,往往让许多人感到迷茫与挑战重重。今天,就让…...

GHelper终极指南:用轻量化工具彻底替代Armoury Crate,释放华硕ROG笔记本全部性能!

GHelper终极指南:用轻量化工具彻底替代Armoury Crate,释放华硕ROG笔记本全部性能! 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RG…...

c++入门:函数实参形参傻傻分不清?如何改变实参!

值传递是 C 中最基本的参数传递方式。它的核心意思是:当你把一个变量作为参数传给函数时,函数得到的是这个变量的一个副本,而不是变量本身。所以在函数内部修改这个副本,外部的原变量纹丝不动。📦 举个生活例子你把一张…...

ObsPy完整指南:如何用Python快速处理地震数据

ObsPy完整指南:如何用Python快速处理地震数据 【免费下载链接】obspy ObsPy: A Python Toolbox for seismology/seismological observatories. 项目地址: https://gitcode.com/gh_mirrors/ob/obspy ObsPy是一个专为地震学和地震观测站设计的Python工具库&…...