Java 用 ScheduledExecutorService 做定时任务:比 Timer 强在哪、异常吞没与优雅关闭
Java 用 ScheduledExecutorService 做定时任务:比 Timer 强在哪、异常吞没与优雅关闭项目里要跑个定时任务:每 5 秒拉一次配置、每分钟清一次缓存。很多人上手就new Timer().schedule(...),能跑就没再管。直到某天线上定时任务莫名其妙全停了——查了半天才发现是某次任务抛了个异常,把整个 Timer 线程干掉了,后面所有任务再也不执行。这篇讲清楚三件事:为什么该用ScheduledExecutorService取代Timer、任务里的异常是怎么被偷偷吃掉的、以及关闭时怎么不丢任务。先看 Timer 的两个致命伤TimertimernewTimer();timer.scheduleAtFixedRate(newTimerTask(){Overridepublicvoidrun(){if(Math.random()0.5){thrownewRuntimeException(模拟任务异常);}System.out.println(任务执行:LocalTime.now());}},0,1000);跑几秒你就会看到:一旦某次run()抛出未捕获异常,整个 Timer 直接崩溃,控制台再无输出。原因是Timer内部只有一个执行线程,异常会终止这个线程,后续所有任务陪葬。第二个坑:Timer 用的是绝对时间。如果系统时钟被回拨(NTP 校时、运维手动改时间),任务调度会错乱。正确写法:ScheduledExecutorServiceScheduledExecutorService用线程池,基于相对延迟调度,是java.util.concurrent里的标准方案。importjava.util.concurrent.*;ScheduledExecutorServiceschedulerExecutors.newScheduledThreadPool(2);// 固定频率:每 1 秒执行一次(从上次开始时间算起)scheduler.scheduleAtFixedRate(()-{System.out.println(fixedRate 任务:LocalTime.now());},0,1,TimeUnit.SECONDS);// 固定延迟:上次执行结束后再等 1 秒(适合任务耗时不稳定的场景)scheduler.scheduleWithFixedDelay(()-{System.out.println(fixedDelay 任务:LocalTime.now());},0,1,TimeUnit.SECONDS);scheduleAtFixedRate和scheduleWithFixedDelay的区别是新手必踩的点:fixedRate:每隔固定周期触发,不管上次跑多久。如果任务耗时超过周期,下次会紧接着执行(任务会堆积)。fixedDelay:等上一次执行完再计时。任务之间永远有固定间隔,不会堆积。拉配置、发心跳这种必须匀速的用 fixedRate;清理、同步这种别重叠就行的用 fixedDelay。关键坑:异常会被静默吞掉这是ScheduledExecutorService最反直觉的地方。它比 Timer 强——一次任务抛异常不会影响别的任务,但它对同一个周期任务的处理是:抛异常后,这个周期任务就再也不调度了,而且异常被默默吞掉,连日志都没有。scheduler.scheduleAtFixedRate(()-{System.out.println(执行前);thrownewRuntimeException(炸了);// 你在控制台看不到这个异常},0,1,TimeUnit.SECONDS);// 只会打印一次执行前,之后这个任务静默消失,程序还继续跑异常去哪了?被包在返回的ScheduledFuture里了,你不get()就永远看不到。所以周期任务里必须自己 try-catch 兜住所有异常,否则任务会悄无声息地阵亡:scheduler.scheduleAtFixedRate(()-{try{doBusiness();// 真正的业务逻辑}catch(Throwablet){// 兜住一切,包括 Error,保证任务能继续下一轮log.error(定时任务执行失败,本轮跳过,t);}},0,1,TimeUnit.SECONDS);记住这条铁律:放进 scheduleAtFixedRate 的 Runnable,内部必须是不抛异常的。任何漏出来的异常都会让这个周期任务永久停摆。优雅关闭:别用 shutdownNow 一刀切程序退出时直接System.exit或者不管线程池,可能导致正在执行的任务被硬中断。正确的关闭姿势分三步:scheduler.shutdown();// 1. 不再接受新任务,已提交的继续执行try{// 2. 给正在跑的任务一点时间收尾if(!scheduler.awaitTermination(10,TimeUnit.SECONDS)){// 3. 超时还没结束,强制中断scheduler.shutdownNow();}}catch(InterruptedExceptione){scheduler.shutdownNow();Thread.currentThread().interrupt();// 恢复中断状态,别吞掉}shutdown():温柔关闭,等已有任务跑完,但周期任务会停止下一次调度。shutdownNow():发中断信号并清空等待队列,适合超时后的兜底。awaitTermination:阻塞等待,给收尾留窗口。Spring 项目里更简单:把 bean 声明成Bean(destroyMethod shutdown),或用PreDestroy触发上面的关闭逻辑,容器停机时自动回收。一个能直接用的封装publicclassSafeScheduler{privatefinalScheduledExecutorServicescheduler;publicSafeScheduler(intpoolSize){// 用带名字的线程工厂,排查问题时能一眼认出是哪个池的线程this.schedulerExecutors.newScheduledThreadPool(poolSize,r-{ThreadtnewThread(r);t.setName(safe-scheduler-t.getId());t.setDaemon(true);// 守护线程,不阻止 JVM 退出returnt;});}publicvoidscheduleAtFixedRate(Runnabletask,longperiod,TimeUnitunit){scheduler.scheduleAtFixedRate(()-{try{task.run();}catch(Throwablet){System.err.println([scheduler] 任务异常,本轮跳过: t.getMessage());}},0,period,unit);}publicvoidshutdown(){scheduler.shutdown();try{if(!scheduler.awaitTermination(10,TimeUnit.SECONDS)){scheduler.shutdownNow();}}catch(InterruptedExceptione){scheduler.shutdownNow();Thread.currentThread().interrupt();}}}小结别再用Timer:单线程、异常会连坐整个调度器、依赖绝对时间。ScheduledExecutorService全面替代它。scheduleAtFixedRate按周期匀速触发(任务会堆积),scheduleWithFixedDelay按上次结束后计时(不重叠),按业务语义选。周期任务内部必须 try-catch 兜住一切异常——漏出来的异常会让这个任务静默停摆,连日志都不给你留。关闭走shutdown()→awaitTermination→ 超时shutdownNow()三段式,给任务收尾留窗口。一句话记忆:ScheduledExecutorService 不会因为一次异常崩掉整个池,但会因为一次异常悄悄弄死那一个周期任务——所以异常绝不能漏出 Runnable。

相关新闻

暗黑破坏神2存档编辑器:免费可视化工具完全指南

暗黑破坏神2存档编辑器:免费可视化工具完全指南

暗黑破坏神2存档编辑器:免费可视化工具完全指南 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 暗黑破坏神2存档编辑器(d2s-editor)是一款功能强大的免费开源工具,专为暗黑破坏神2…

2026/10/17 23:33:27 阅读更多 →
React 长列表虚拟滚动实战:手写可视区渲染,10 万条数据不卡顿

React 长列表虚拟滚动实战:手写可视区渲染,10 万条数据不卡顿

React 长列表虚拟滚动实战:手写可视区渲染,10 万条数据不卡顿 后台管理系统里经常遇到这种需求:一次性拉回来几万条日志、几万个 SKU,产品说"不想分页,就要滚动加载全部"。你老老实实 list.map() 渲染出来,浏览器直接卡死——10 万个 DOM 节点,首屏白屏两秒,滚动时掉…

2026/10/17 23:43:20 阅读更多 →
Unity粒子系统实现Logo动态特效:从路径点收敛到性能优化全解析

Unity粒子系统实现Logo动态特效:从路径点收敛到性能优化全解析

1. 项目概述:为什么动态Logo特效是品牌视觉的“点睛之笔”在数字内容创作,尤其是游戏、应用启动界面、品牌展示视频中,一个静态的Logo往往显得过于“安静”。当用户点击启动按钮,或者进入一个关键场景时,一个能够“呼吸…

2026/10/17 23:43:30 阅读更多 →

最新新闻

用图论解析网络攻击路径:建模、算法与落地实践

用图论解析网络攻击路径:建模、算法与落地实践

提到“基于图论的网络攻击路径分析”,很多人第一反应是学术论文里才有的概念。但我在实际安全评估中,用这套方法解决过非常具体的问题:某企业准备攻防演练前,安全负责人问我,假设最边缘的一台办公终端已经被攻破&#…

2026/10/18 2:08:56 阅读更多 →
Dify实战:大模型应用开发、RAG知识库与Agent工作流指南

Dify实战:大模型应用开发、RAG知识库与Agent工作流指南

1. Dify是什么:先解决一个最实际的问题先说个场景。一个做电商运营的朋友,想搞一个"客服机器人",能自动回答用户关于退货政策、物流进度、优惠券使用的问题。他一开始的路径是:找大模型API、写Prompt、建知识库、处理对…

2026/10/18 2:08:56 阅读更多 →
C#上位机通过MX Component读写三菱PLC D区和M区实战指南

C#上位机通过MX Component读写三菱PLC D区和M区实战指南

做上位机的人,几乎都会遇到要和PLC打交道的场景。前段时间接了一个项目,要求用C#写一套设备数据采集程序,把现场几十台PLC的工艺参数和生产状态实时读上来,存到数据库里供MES调用。对方只给了一个要求:用三菱的MX Comp…

2026/10/18 2:08:55 阅读更多 →
Flutter ScaffoldMessenger实战:SnackBar消息通知机制与避坑指南

Flutter ScaffoldMessenger实战:SnackBar消息通知机制与避坑指南

如果你最近在 Flutter 项目里维护消息通知,一定对 ScaffoldMessenger 不陌生。这个组件从 Flutter 2.0 开始就成为 SnackBar 的官方入口,但很多人只是照着文档写ScaffoldMessenger.of(context).showSnackBar(...),真到项目复杂起来——页面嵌…

2026/10/18 2:08:55 阅读更多 →
TensorFlow Lite端侧推理实战:从模型转换到Android/iOS集成与性能调优

TensorFlow Lite端侧推理实战:从模型转换到Android/iOS集成与性能调优

1. 从训练到部署:为什么端侧推理是移动AI的最后一公里做过移动端AI应用的人都有一个共同感受:模型在服务器上跑得好好的,一放到手机上就各种水土不服。训练阶段用的是高端显卡,推理阶段却要面对手机芯片那点可怜的内存和算力。这个…

2026/10/18 2:08:55 阅读更多 →
TensorFlow Lite移动端部署实战:量化、性能调优与热替换

TensorFlow Lite移动端部署实战:量化、性能调优与热替换

1. 移动端推理框架选型的核心逻辑做过移动端AI功能的人都有一个共识:在手机上跑模型,跟在服务器上跑模型完全是两码事。服务器上你可以堆显卡、加内存,实在不行换张更猛的卡就行。但手机端不行,你得在算力、内存、功耗、发热这四个…

2026/10/18 2:07:55 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/16 14:23:36 阅读更多 →