JUC下的ScheduledThreadPoolExecutor详解

         ScheduledThreadPoolExecutor是Java并发编程框架中一个强大且灵活的线程池实现,专为定时与周期性任务而设计。作为ThreadPoolExecutor的子类,它不仅继承了线程池管理的高效与灵活性,还内置了基于优先级队列的延迟任务调度机制,支持任务的定时执行、固定速率执行以及固定延迟执行。通过使用ScheduledThreadPoolExecutor,开发者可以方便地安排一次性或重复性的后台任务,同时得益于线程池的特性,有效复用了线程资源,减少了线程创建销毁的开销,提升了程序性能与响应速度。它广泛应用于诸如定时数据处理、定时检查与维护、定时消息推送等多种场景,是构建高可靠、高性能后台服务不可或缺的工具之一。

一、详细介绍

  ScheduledThreadPoolExecutor是Java并发包java.util.concurrent中的一个类,它是专门为定时和周期性任务执行而设计的线程池。它继承自ThreadPoolExecutor,因此具备了线程池的所有特性,同时增加了任务调度的功能。它使用一个无界优先队列来存储待执行的任务,这些任务根据它们的延时或周期进行排序。

1、类继承结构与核心组件

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,是专为定时和周期性任务设计的增强版线程池。它内部维护了一个DelayedWorkQueue(延迟工作队列),这是一个基于优先堆的无界队列,用于存储实现了Delayed接口的任务。每个入队的任务都会根据其getDelay(TimeUnit.NANOSECONDS)方法计算的延迟时间进行排序,队列顶部总是具有最早执行时间的任务。

2、核心方法
  • schedule: 安排在指定延迟后执行给定的任务一次。
  • scheduleAtFixedRate: 安排指定任务按照固定的间隔周期执行,首次执行在指定的延迟后开始,后续执行在上一次执行结束后再经过指定的周期开始。需要注意的是,如果任务执行时间超过了周期长度,下一次执行将在当前任务结束时立即开始,而不是严格按照周期间隔。
  • scheduleWithFixedDelay: 与scheduleAtFixedRate类似,但不同之处在于,如果任务执行时间超过了间隔周期,那么下一次执行将在当前任务结束后的固定延迟后开始,而不是紧接着执行。
3、内部工作原理
  1. 任务入队: 当通过上述方法安排任务时,ScheduledThreadPoolExecutor会创建一个ScheduledFutureTask,它既是Runnable也是Future,还实现了Delayed接口,允许根据执行时间排序。这个任务会被放入DelayedWorkQueue中。

  2. 任务调度: 线程池中的工作者线程会不断从DelayedWorkQueue中取出最早到期的任务进行执行。如果队列为空,则工作者线程会阻塞等待直到有任务到达其执行时间。

  3. 时间准确性: 为了提高定时任务的准确性,ScheduledThreadPoolExecutor内部使用了LockSupport.parkNanos方法进行精准的纳秒级等待,减少由于线程调度或系统负载导致的时间偏移。

4、线程池参数调整
  • corePoolSize: 核心线程数,即使没有任务执行,也会保持存活的线程数量。
  • maximumPoolSize: 线程池最大线程数,超过这个数的多余任务将被排队等待。
  • keepAliveTime: 非核心线程闲置时的超时时长,超过此时间会回收线程,仅对非核心线程有效。
  • unit: 上述参数中时间单位。
  • workQueue: 任务队列,ScheduledThreadPoolExecutor默认使用的是无界的DelayedWorkQueue
  • threadFactory: 线程工厂,用于创建新线程。
  • handler: 拒绝策略,当线程池和队列都满时,用来处理新提交的任务。
5、性能与优化
  • 任务粒度: 尽量使任务粒度适中,过细的任务会增加调度开销,过粗则可能导致资源利用率不高。
  • 资源限制: 考虑系统资源限制,合理设置线程池大小,避免资源耗尽或过度竞争。
  • 异常处理: 在任务中妥善处理异常,避免因单个任务失败导致整个线程池或工作队列受到影响。

二、使用场景 

1、定时任务
  1. 数据备份与清理: 定时自动备份数据库或清理过期日志文件,保持系统运行环境的整洁和数据的安全性。

  2. 系统维护: 如定期执行磁盘空间检查、数据库索引优化、系统健康检查等,确保系统长期稳定运行。

  3. 报告生成: 定时生成业务报表、数据分析报告,例如每日、每周或每月的销售报表,便于管理层及时了解业务状况。

2、周期性任务
  1. 心跳检测: 在分布式系统中,客户端或服务端之间的定期心跳检测,维持连接活跃,及时发现网络异常。

  2. 缓存更新: 定时刷新或更新缓存内容,如商品价格、用户信息等,确保数据的实时性和一致性。

  3. 消息推送: 定时拉取或推送消息,如新闻推送、系统通知、邮件发送等,自动化处理信息传递。

3、计划任务
  1. 资源调度: 在云计算平台中,按计划调度资源,如自动扩展或缩减云服务器实例,依据流量高峰低谷动态调整资源。

  2. 系统定时维护窗口: 在特定时间自动开启或关闭服务,如夜间进行数据库维护、系统升级等,减少对用户的影响。

  3. 定时任务调度平台: 构建企业级的任务调度中心,允许业务方提交定时任务,统一管理执行,如定时作业调度、ETL流程等。

4、实时性要求不高的定时处理
  • 数据分析与统计: 对历史数据进行定期分析,如日志分析、用户行为分析等,生成分析报告或更新数据视图。

  • 定时爬虫: 定期抓取网页内容,用于信息收集、竞争对手监控、舆情分析等,避免频繁访问导致目标网站压力过大。

5、特定行业应用
  1. 金融行业: 定时处理交易清算、账户余额更新、风险评估等,确保金融业务的准确性和安全性。

  2. 电商行业: 商品库存更新、订单状态检查、促销活动定时开启与结束等,保障购物流程顺畅。

  3. 物联网(IoT): 设备状态监测、数据收集、远程控制指令发送等,定时维护物联网设备的正常运作。

        注意:在选择使用ScheduledThreadPoolExecutor时,需综合考虑任务的性质(如是否允许任务堆积、任务执行时间是否可预测)、系统资源限制以及任务执行失败的处理策略,确保定时任务的高效、稳定执行。同时,对于任务执行频率极高或对执行时间精度要求严格的场景,可能需要额外考虑使用更为专业的定时任务调度框架或结合其他技术手段来满足需求。

三、使用示例(Java):

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledThreadPoolExecutorExample {public static void main(String[] args) {// 创建一个定长线程池,支持定时及周期性任务执行ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);// 延迟3秒执行一次scheduledThreadPool.schedule(new RunnableTask("Delayed Task"), 3, TimeUnit.SECONDS);// 周期性执行,首次延迟3秒,之后每2秒执行一次scheduledThreadPool.scheduleAtFixedRate(new RunnableTask("Fixed Rate Task"), 3, 2, TimeUnit.SECONDS);// 周期性执行,首次立即执行,之后每隔2秒执行一次scheduledThreadPool.scheduleWithFixedDelay(new RunnableTask("Fixed Delay Task"), 0, 2, TimeUnit.SECONDS);}static class RunnableTask implements Runnable {private final String name;RunnableTask(String name) {this.name = name;}@Overridepublic void run() {System.out.println("Task " + name + " is running at " + System.currentTimeMillis());}}
}

 四、注意事项

1、异常处理

捕捉并妥善处理异常:如前所述,当ScheduledThreadPoolExecutor执行的任务抛出未捕获的异常时,该任务会被取消,不再继续执行。因此,务必在任务实现中使用try-catch块捕获Throwable,以防止单个任务的失败影响到整个定时任务的连续执行。可以考虑在catch块中记录日志并决定是否重新调度失败的任务。

2、线程池配置

合理配置线程池大小:核心线程数应根据任务特性和系统资源进行合理配置。过多的核心线程可能导致资源过度消耗,而过少则可能无法充分利用系统资源或导致任务排队等待时间过长。对于周期性任务,考虑任务执行时间和间隔,避免因线程不足导致任务堆积。

3、任务调度策略

理解调度策略差异scheduleAtFixedRatescheduleWithFixedDelay有显著区别。前者确保任务按照固定周期执行,即使前一次执行延迟,下一次执行也会尽快开始,可能导致任务并发执行。后者则是在上一次任务执行结束后再经过固定延迟才开始下次执行,更适用于需要确保任务间有一定间隔的情况。

4、资源释放与内存泄漏

避免资源泄露:任务执行完毕后,确保释放所有资源,包括关闭数据库连接、文件流等,防止内存泄漏。对于使用内部类或匿名类作为任务时,注意它们对外部变量的引用可能导致的潜在内存泄漏问题。

5、任务取消与终止

提供任务取消机制:利用Future接口提供的cancel方法,允许外部逻辑根据需要取消任务。同时,了解如何正确地关闭ScheduledThreadPoolExecutor,使用shutdownshutdownNow方法,并处理好后续的清理工作。

6、任务优先级与排序

注意任务排序:虽然ScheduledThreadPoolExecutor使用DelayQueue来保证任务按照延时顺序执行,但具体到相同延时的任务,执行顺序依赖于队列内部排序。若对任务有特定的优先级需求,可能需要自定义比较逻辑或使用其他调度策略。

7、监控与日志

监控与日志记录:实施有效的监控机制,跟踪任务执行状态、执行时长、线程池负载等,这对于诊断问题和性能调优至关重要。同时,详细日志记录能帮助快速定位问题。

8、系统稳定性考量

考虑系统稳定性:在设计任务时,应考虑到系统的整体稳定性,避免因单个任务长时间阻塞导致整个线程池无法处理其他任务。可以为长耗时任务设置超时处理,或者将其拆分为更小的任务单元。

五、优缺点

1、优点
  • 高效资源利用:通过重用线程池中的线程,减少了线程创建和销毁的开销,提高了系统效率和响应速度,特别是在任务执行频繁且短时的场景下效果显著。

  • 灵活的任务调度:支持多种任务调度模式,包括一次性执行、固定速率执行和固定延迟执行,满足不同应用场景的需求,如定时任务、周期性作业等。

  • 强大的扩展性:作为ThreadPoolExecutor的子类,继承了其全部功能,如自定义线程工厂、拒绝策略等,可以根据具体需求进行高度定制化配置。

  • 易于使用:通过Executors工厂类,可以快速创建一个ScheduledThreadPoolExecutor实例,降低了定时任务编程的复杂度,提高了开发效率。

  • 精确的延时与周期控制:内部使用了DelayedWorkQueue,能够精确控制任务的执行时机,尽管受制于系统调度,但相比传统的定时器类,提供了更精细的控制能力。

2、缺点
  • 资源限制与潜在的内存泄漏:作为一个无界队列,理论上可以无限添加任务,若任务产生速度远大于处理速度,可能导致内存持续增长,最终引发内存溢出。此外,未正确管理的资源(如未关闭的数据库连接)也可能引起内存泄漏。

  • 任务执行时间的影响scheduleAtFixedRate方法中,若任务执行时间超过预定周期,后续任务的执行时间将被压缩,可能导致系统负载激增。特别是在任务执行时间不稳定的情况下,难以保证任务按预期频率执行。

  • 定时不精确:尽管ScheduledThreadPoolExecutor尽可能准确地执行任务,但由于线程调度、系统负载、垃圾回收等因素,实际执行时间可能会有轻微偏移,对于时间敏感型应用可能不够理想。

  • 调试与监控困难:相较于直接管理线程的编程方式,使用线程池后,任务的执行轨迹和异常处理更加隐式,增加了问题排查的难度。需要额外的监控和日志机制来辅助调试。

  • 潜在的死锁与活锁风险:不当的任务设计或资源竞争可能导致线程死锁或活锁,尤其是在任务间存在复杂依赖关系的情况下,需要特别注意同步和并发控制。

六、可能遇到的问题及解决方案

1. 任务堆积与资源耗尽

问题描述:当提交的任务速率远高于线程池处理速率时,即使使用了无界队列DelayedWorkQueue,也可能导致任务队列无限增长,最终耗尽系统资源。

解决方案

  • 限制任务队列大小:考虑使用有界队列如ArrayBlockingQueue替代默认的无界队列,通过限制队列大小,可以避免无限制的任务积累。
  • 监控任务队列长度:实施监控机制,当队列长度达到预警阈值时,采取相应措施,如动态调整线程池大小或暂时拒绝新任务。
  • 拒绝策略调整:使用或自定义拒绝策略,如AbortPolicy直接抛出异常,CallerRunsPolicy让调用者线程执行任务,或记录日志后忽略新任务。
2. 定时不准确

问题描述:由于系统调度、垃圾回收、任务执行时间波动等因素,定时任务的实际执行时间可能与预期有偏差。

解决方案

  • 调整线程池大小:根据任务特点和系统资源,合理设置线程池大小,确保有足够的线程处理任务,减少排队等待时间。
  • 优化任务执行:缩短单个任务执行时间,减少其对后续任务的影响。对于耗时任务,考虑拆分或异步处理。
  • 使用更精确的调度器:在极端情况下,可能需要考虑使用外部专门的定时调度服务或库,以获得更精确的定时控制。
3. 异常处理不当

问题描述:任务执行中未妥善处理的异常会导致任务取消,线程终止,甚至整个线程池受到影响。

解决方案

  • 全面的异常捕获:在任务执行代码中使用try-catch包裹,确保所有异常都能被捕获并处理。
  • 记录日志与重试机制:捕获异常后记录详细日志,并根据任务性质考虑是否需要重试机制。
  • 定制化线程工厂:使用自定义线程工厂,在创建线程时设置合适的未捕获异常处理器,如Thread.setDefaultUncaughtExceptionHandler
4. 线程泄露与内存泄漏

问题描述:任务中未正确关闭资源或存在循环引用,可能导致线程无法回收,引发内存泄漏。

解决方案

  • 资源管理:确保任务执行完毕后,所有资源(如数据库连接、文件流)都被正确关闭。
  • 弱引用或软引用:对于任务中使用的大型对象,考虑使用弱引用或软引用,以降低内存泄漏风险。
  • 定期审查代码:定期进行代码审查,查找并修复潜在的内存泄漏问题。
5. 线程池管理与监控不足

问题描述:缺乏对线程池运行状态的有效监控,难以及时发现和解决问题。

解决方案

  • 实施监控:利用Java自带的管理接口(如ThreadPoolExecutorgetQueue()getActiveCount()等方法)或第三方监控工具(如Prometheus+Grafana)实施监控。
  • 报警机制:设置阈值报警,当线程池状态(如任务队列长度、线程池大小、拒绝次数)超过预设值时,发送警报通知相关人员。
  • 定期审计:定期审查线程池配置和任务执行情况,根据系统负载和性能指标进行适时调整。

        通过合理设计和使用ScheduledThreadPoolExecutor,可以有效地在Java应用中实现定时和周期性任务的高效执行。ScheduledThreadPoolExecutor是一个强大且灵活的定时任务执行框架,适合处理大量定时和周期性任务。然而,其使用时需要充分考虑资源管理、任务调度细节以及异常处理机制,以确保系统稳定高效运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/8553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于模糊PI控制算法的龙格库塔CSTR模型控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于模糊PI控制算法的龙格库塔CSTR模型控制系统simulink建模与仿真。基于模糊PI控制算法的龙格-库塔(Runge-Kutta, RK)连续搅拌釜反应器(Co…

景源畅信电商:抖音小店需要请专业人员装修店铺吗?

在数字营销的海洋中,抖音小店如一艘航船,装修得当才能吸引顾客登船。那么,小店是否需要请专业人员来装修呢?答案是肯定的。 一、视觉冲击力是关键 专业设计师擅长运用色彩、布局与图像创造出强烈的视觉冲击力,这对于抓住用户的注…

2024vue官方生态最全整理

👩 个人主页:程序媛夏天 🙋‍♂️ 作者简介:前端领域新星创作者、CSDN内容合伙人、自媒体职场博主,专注于前端各领域技术,成长的路上共同学习共同进步,一起加油呀! ✨系列专栏:前端面试宝典、JavaScript进阶、vue实战 📢 资料领取:前端进阶资料以及文中源码可以在…

【四、性能测试】性能测试基础与几个重要的概念

你好,我是山茶,一个95后在职程序员。也是一个目标跟 1000 程序员探索出 AI 测试 副业之路的 bro,欢迎跟我一起沟通交流! 一、什么是性能测试? 性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来…

【Linux——Centos7安装RabbitMQ】 RabbitMQ无法连接

到这一步是基本已经装好了,现在是在开放端口,我这个报错是因为我的防火墙是处于关闭状态,所以在开放端口时会报防火墙为运行,把防火墙打开,在开放端口,就可以访问到了 重启防火墙: systemctl …

MATLAB的Bar3函数调节渐变色

一. colormap函数 可以使用colormap函数: t1=[281.1,584.6, 884.3,1182.9,1485.2; 291.6,592.6,896,1197.75,1497.33; 293.8,596.4,898.6,1204.4,1506.4; 295.8,598,904.4,1209.0,1514.6];bar3(t1,1) set(gca,XTickLabel,{300,600,900,1200,1500},FontSize,10) set(gca,YTic…

常见C语言基础题说明一

一. 简介 整理一些C语言常见的基础笔试题。 假如系统是 32位计算机的情况下,字节对齐方式为 4字节对齐。计算如下题目。 二. 常见C语言基础笔试题 1. 计算 sizeof(数组名), sizeof(指针),strlen(数组名) 大小 计算如下的变量的值&…

npm无法安装node-sass 的问题

安装 node-sass 的问题呈现:4.9.0版本无法下载 Downloading binary from https://github.com/sass/node-sass/releases/download/v4.9.0/win32-x64-72_binding.node Cannot download "https://github.com/sass/node-sass/releases/download/v4.9.0/win32-x64-…

技术速递|介绍 .NET MAUI 社区工具包 v8 :包含 TouchBehavior 支持!

作者:Gerald Versluis 排版:Alan Wang .NET MAUI 社区工具包团队很自豪地向您介绍 .NET MAUI 社区工具包的第 8 版! 在这个最新的主要版本中,我们为您带来了备受期待的 TouchBehavior(以前称为 TouchEffect&#xff0…

MM模块学习一(供应商创建,物料类型的定义及功能)

物料管理流程: 源头:采购需求->采购申请 MRP:物料需求计划。运行物料需求计划的结果,根据物料的性质来判断是外购(采购申请)或者是生产(计划订单->生产订单)。 采购申请&am…

在Linux中安装Docker

如果之前安装过旧版本的 Docker,可以使用下面命令卸载: yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-selinux \docker-engine-selinux \docker-engine…

500的项目研发成本2000?

上个月接了一个小程序的二开项目,功能不多就2个诉求:调整首页数据排序规则,帖子详情增加一个海报,报了一个我认为还比较合适的价格500。 当我拿到代码的那一刻有点小害怕,因为这个客户的之前合作过一次,项…

17.接口自动化学习-日志

1.日志输出渠道 &#xff08;1&#xff09;文件格式 xx.log &#xff08;2&#xff09;控制台输出 2.日志级别 debug<info<warnning<error<critical 3.代码实现 from utils.handle_path import log_path import logging import datetime def logger(fileLogTr…

数值计算方法——大题题型总结

目录 一、绝对误差限、相对误差限 1.1 例题 1.2 解题套路 1.3 题解 二、敛散性、收敛速度 2.1 例题 2.2 解题套路 2.3 题解 三、牛顿迭代法 3.1 例题 3.2 解题套路 3.3 题解 四、割线法 4.1 例题 4.2 解题套路 ​4.3 题解 五、列主元素消去法 5.1 例题 5.…

Ansible——playbook编写

一、简介 1.什么是playbook Ansible Playbook 是设定自动化任务的一种蓝图&#xff0c;可在无需人工干预或有限干预的前提下执行复杂的 IT 操作。Ansible Playbook 对一组或一类共同构成 Ansible 清单的主机执行。 Ansible Playbook 本质上是一些框架&#xff0c;是一些预先编…

多线程基础知识(全面):创建线程、线程状态如何变化、wait()、notify()、sleep()、停止线程

文章目录 一、创建线程的四种方式1.1 继承Thread类1.2 实现runnable接口1.3 实现Callable接口1.4 线程池创建线程1.5 补充&#xff1a;runnable、callable都可以创建线程&#xff0c;有什么区别&#xff1b;run()和 start()有什么区别 二、线程包括哪些状态、状态之间如何变化2…

书单 | 6本AI领域名家名作,大模型时代,趁风而起!

–文末赠书– 大模型时代&#xff0c;想抓住风口吗&#xff1f; 本期书单就来分享6本AI领域名家名作&#xff0c;给大家把大模型时代那些事儿讲清楚&#xff01; 放心&#xff0c;入门的同学也可以从最基础的学起~~ 快来看看有哪些书吧…… 01 ▊《多模态大模型&#xff1…

PCIe下一代线缆标准CopprLink发布

作为业界广泛采用的高速串行点对点互联标准&#xff0c;PCIe自诞生以来历经多次迭代升级&#xff0c;现已成为CPU、GPU、FPGA、SSD等计算设备间不可或缺的互连桥梁。PCIe 7.0标准更是将数据传输速率提升至令人惊叹的32 GB/s&#xff08;每通道&#xff09;。 然而&#xff0c;面…

PPT弹簧画法

1. 插入两个圆 2. 使用Lvyhtools的形状-位置分布-圆形阵列 注意:阵列中心要点击文字后才能选择 3. 删除中心的圆,使用Onekey10的原位复制,可以多次; 4. 右击图像选择设置形状格式-线条(无线条) 5. 找到第二个选项,深度设置大小为0.3-0.6磅 6. 再次到Onekey10界面,选择…

深度解析DPO及其变体在多种任务上的表现如何,该如何选择

深度学习自然语言处理 原创作者&#xff1a;wkk 单位&#xff1a;亚利桑那州立大学paper&#xff1a;Insights into Alignment:Evaluating DPO and its Variants Across Multiple TasksLink&#xff1a;https://arxiv.org/pdf/2404.14723 今天&#xff0c;我要带大家深入了解一…