写点什么

SingleThreadScheduledExecutor 线程池设计 / 场景案例 / 性能调优 / 场景适配(架构篇)

作者:肖哥弹架构
  • 2024-11-08
    河北
  • 本文字数:3846 字

    阅读完需:约 13 分钟

SingleThreadScheduledExecutor线程池设计/场景案例/性能调优/场景适配(架构篇)


在多线程编程中,我们经常会遇到需要按特定顺序执行任务的场景,例如定时任务、周期性任务或者保证任务执行顺序的情况。SingleThreadScheduledExecutor 正是为了满足这样的需求而设计的。它提供了一个单一的后台线程,用于顺序执行所有提交的任务,确保了任务的执行顺序与提交顺序相同。这种线程池特别适用于那些对任务执行顺序有严格要求的应用,如日志处理、事件调度等。通过 SingleThreadScheduledExecutor,我们可以轻松地安排任务在未来的某个时间点执行,或者以固定的频率重复执行,同时保持代码的简洁性和易于管理。


肖哥弹架构 跟大家“弹弹” 高并发锁, 关注公号回复 'mvcc' 获得手写数据库事务代码

欢迎 点赞,关注,评论。

关注公号 Solomon 肖哥弹架构获取更多精彩内容

历史热点文章

1、SingleThreadScheduledExecutor 制造背景

SingleThreadScheduledExecutor 是 Java 中的一个单线程调度线程池,它保证所有调度任务在单个线程中按顺序执行。以下是其设计因素的概述:


  1. 顺序执行

  2. SingleThreadScheduledExecutor 提供了一个单一的线程来顺序执行所有任务,这对于需要保证任务执行顺序的场景非常有用,比如在处理需要按特定顺序执行的日志记录或事件处理时。

  3. 资源优化

  4. 由于只有一个线程在运行,这种线程池可以减少多线程环境下的资源竞争和上下文切换的开销,从而提高效率。

  5. 简化线程管理

  6. 它简化了线程的创建和管理,开发者不需要手动创建和销毁线程,SingleThreadScheduledExecutor 会自动管理单个工作线程的生命周期。

  7. 适用于轻量级任务

  8. 适用于那些执行时间较短、需要频繁调度的轻量级任务,例如定期检查、状态更新等。

  9. 异常安全

  10. 如果任务执行过程中出现异常,SingleThreadScheduledExecutor 会抑制后续任务的执行,除非异常被显式捕获和处理。

  11. 定时和周期性任务

  12. 支持延迟执行任务以及周期性重复执行任务,这使得它非常适合需要定时或周期性执行的场景。

  13. 线程安全性

  14. 由于所有任务都在单个线程中执行,SingleThreadScheduledExecutor 自然保证了任务之间的线程安全性,无需额外的同步措施。

2、SingleThreadScheduledExecutor 设计结构

单个线程的变体,用于延迟或定时执行任务。



  1. SingleThreadScheduledExecutor:这是单线程的调度线程池,负责管理单个工作线程和任务的执行。

  2. 单个工作线程:线程池中只有一个工作线程,负责执行所有提交的任务。

  3. 任务队列(DelayedWorkQueue) :用于存储待执行任务的延迟队列。

  4. 线程工厂:用于创建新线程的工厂。

  5. 拒绝策略处理器:当任务队列满且工作线程忙碌时,用于处理新提交任务的策略。

  6. 任务提交:任务提交到线程池执行。

  7. ScheduledFutureTask:表示可以延迟执行的异步运算任务。

  8. 执行任务:工作线程从任务队列中取出任务并执行。

  9. 重新调度:对于周期性任务,执行完毕后重新调度下一次执行。

  10. 线程空闲或销毁:任务执行完毕后,线程可能变为空闲状态,等待新任务,或者在线程池关闭时被销毁。

  11. 线程池终止:当线程池关闭时,所有线程将停止执行任务,并等待已提交的任务完成。

3、SingleThreadScheduledExecutor 运行流程


SingleThreadScheduledExecutor 的运行流程:


  1. 创建 SingleThreadScheduledExecutor 实例:使用 Executors.newSingleThreadScheduledExecutor() 方法创建一个单线程调度线程池实例。

  2. 提交任务:通过 schedulescheduleWithFixedDelayscheduleAtFixedRate 方法提交任务。

  3. 任务封装为 ScheduledFutureTask:提交的任务被封装为 ScheduledFutureTask 对象。

  4. 任务存储于 DelayedWorkQueueScheduledFutureTask 对象被存储在 DelayedWorkQueue 队列中,根据预定执行时间排序。

  5. 到达预定时间:等待直到任务的预定执行时间到达。

  6. 任务执行:单线程执行任务。

  7. 是否周期性任务:检查任务是否需要周期性执行。

  8. 重新调度任务:如果是周期性任务,重新调度下一次执行。

  9. 任务完成:非周期性任务执行完毕后,任务完成。

  10. 关闭 ScheduledExecutorService:当不再需要线程池时,调用 shutdown 方法关闭线程池。

  11. 等待任务完成:调用 awaitTermination 方法等待所有已提交的任务完成。

  12. 线程资源释放:所有任务完成后,线程资源被释放。

4、SingleThreadScheduledExecutor 业务实战

4.1. 定时任务执行

SingleThreadScheduledExecutor 可以用于执行定时任务,例如,一个应用需要每天凌晨自动清理日志文件。


ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();executor.scheduleAtFixedRate(() -> {    // 清理日志文件的代码}, 0, 24, TimeUnit.HOURS); // 每天执行一次
复制代码

4.2. 周期性数据刷新

在需要周期性刷新数据的场景下,例如,一个实时监控系统需要每分钟刷新一次数据。


ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();scheduler.scheduleAtFixedRate(() -> {    // 刷新数据的代码}, 0, 1, TimeUnit.MINUTES); // 每分钟执行一次
复制代码

4.3. 延迟任务执行

SingleThreadScheduledExecutor 也适用于延迟任务执行,比如,一个用户操作后需要在一定时间后发送反馈。


ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();service.schedule(() -> {    // 发送用户反馈的代码}, 10, TimeUnit.SECONDS); // 10秒后执行
复制代码

4.4. 顺序执行任务队列

当任务之间存在依赖关系,需要按特定顺序执行时,SingleThreadScheduledExecutor 可以保证任务的顺序执行。


ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();executor.schedule(() -> {    // 第一个任务}, 0, TimeUnit.SECONDS);executor.schedule(() -> {    // 第二个任务,依赖第一个任务的结果}, 5, TimeUnit.SECONDS);
复制代码

4.5. 避免并发执行

在某些情况下,需要确保任务不会并发执行,而是顺序执行,SingleThreadScheduledExecutor 提供了这样的保证。


ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();scheduler.scheduleWithFixedDelay(() -> {    // 执行顺序敏感的任务}, 0, 5, TimeUnit.SECONDS); // 每5秒执行一次
复制代码

5、SingleThreadScheduledExecutor 调优策略

针对 SingleThreadScheduledExecutor 的调优策略,以下是一些关键点和最佳实践:


  1. 合理配置核心线程数

  2. SingleThreadScheduledExecutor 核心线程数设置为 1,因为它确保所有任务按顺序在一个线程中执行。

  3. 选择合适的工作队列

  4. 默认使用 DelayedWorkQueue 作为任务队列,这是一个无界队列,可以容纳任意数量的任务。如果任务提交速度超过处理速度,应考虑使用有界队列以避免内存溢出。

  5. 处理线程空闲超时

  6. keepAliveTime 参数定义了非核心线程空闲时在终止前的等待时间。对于 SingleThreadScheduledExecutor,这个参数通常不需要设置,因为它只有一个核心线程。

  7. 优雅关闭线程池

  8. 使用 shutdown() 方法来优雅地关闭 SingleThreadScheduledExecutor,确保所有已提交的任务都能执行完毕。如果需要立即停止,可以使用 shutdownNow(),但这可能会导致正在执行的任务被中断。

  9. 监控线程池状态

  10. 监控 SingleThreadScheduledExecutor 的运行状态,如活动线程数、任务队列长度等,可以帮助及时发现性能瓶颈和异常情况,并进行相应的调优。

  11. 自定义线程工厂

  12. 通过自定义线程工厂(ThreadFactory),可以为线程设置有意义的名称,这有助于在出现问题时快速定位问题线程。

  13. 合理配置拒绝策略

  14. 当任务队列满且达到最大线程数时,RejectedExecutionHandler 会介入。可以根据业务需求选择合适的拒绝策略,如 AbortPolicyCallerRunsPolicy 等。

  15. 周期性任务的精确度

  16. 对于需要精确执行周期性任务的场景,应考虑任务执行时间和系统负载对调度精度的影响。scheduleAtFixedRatescheduleWithFixedDelay 提供了不同的周期性执行策略,应根据具体需求选择。

6、SingleThreadScheduledExecutor 适应场景

SingleThreadScheduledExecutor 适用于以下场景:


  1. 顺序执行任务: 当需要保证任务按照特定的顺序执行时,SingleThreadScheduledExecutor 可以确保所有任务都在单个线程中顺序执行,避免并发执行带来的问题。

  2. 定时和周期性任务: 适用于需要单个后台线程执行周期性任务的场景,如定时数据备份、定时发送通知等。

  3. 任务执行的顺序性: 在需要保证任务顺序性的应用中,如日志处理、事件处理等,SingleThreadScheduledExecutor 可以确保任务的顺序执行。

  4. 资源受限环境: 在资源受限的环境中,如嵌入式系统或移动设备,SingleThreadScheduledExecutor 可以有效地管理资源,因为它只有一个线程在运行。

  5. 避免线程竞争: 当任务执行需要访问共享资源,并且需要避免线程间的竞争时,使用 SingleThreadScheduledExecutor 可以减少同步的开销。

  6. 简化错误处理: 由于所有任务都在单个线程中执行,错误处理和调试变得更加简单,因为不需要处理多线程环境下的复杂性。

  7. 轻量级任务处理: 对于执行时间短、数量多的轻量级任务,SingleThreadScheduledExecutor 提供了一种高效的处理方式,避免了频繁创建和销毁线程的开销。

  8. 避免线程饥饿: 在多线程环境中,SingleThreadScheduledExecutor 可以避免线程饥饿问题,因为它只有一个线程在执行任务。

发布于: 刚刚阅读数: 6
用户头像

智慧属心窍之锁 2019-05-27 加入

擅长于通信协议、微服务架构、框架设计、消息队列、服务治理、PAAS、SAAS、ACE\ACP、大模型

评论

发布
暂无评论
SingleThreadScheduledExecutor线程池设计/场景案例/性能调优/场景适配(架构篇)_Java_肖哥弹架构_InfoQ写作社区