深入理解和使用定时线程池ScheduledThreadPoolExecutor

文章目录

  • 前言
  • 认识定时线程池
    • 什么是定时线程池?
    • 定时线程池基本API使用
    • 定时线程池的应用场景
      • 1、定时任务调度
      • 2、缓存过期清理
      • 3、心跳检测
      • 4、延迟任务执行
  • 定时线程池scheduleAtFixedRate与scheduleWithFixedDelay区别
    • scheduleAtFixedRate案例demo(period,任务之间间隔)
    • scheduleWithFixedDelay案例demo(delay,上一个任务结束后间隔时间)
  • ScheduledThreadPoolExecutor源码
    • 如何实现队列任务的捞取(即任务的调度和执行)?
      • 认识DelayedWorkQueue
      • 理解DelayedWorkQueue#take
      • 捞取和执行流程
  • 参考文章
  • 资料获取

前言

博主介绍:✌目前全网粉丝3W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

CSDN搜索:长路

视频平台:b站-Coder长路

本章节配套源码:

  • gitee:https://gitee.com/changluJava/demo-exer/tree/master/JUC/src/main/java/demo12

认识定时线程池

什么是定时线程池?

定时线程池是一种专门用于执行定时任务的线程池,它结合了线程池的优势和定时任务的功能,能够高效地管理和调度任务。

定时线程池是一种特殊的线程池,它不仅可以执行普通任务,还可以安排任务在未来某个时间点执行,或者以固定的速率重复执行。

在Java中,ScheduledThreadPoolExecutor是实现定时线程池的核心类。


定时线程池基本API使用

1、创建定时线程池

ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); // 创建一个包含5个线程的定时任务线程池

2、提交一次性任务

executor.schedule(new Task("one-time"), 1, TimeUnit.SECONDS); // 在1秒后执行任务

3、提交固定速率任务

executor.scheduleAtFixedRate(new Task("fixed-rate"), 2, 3, TimeUnit.SECONDS); // 在2秒后开始执行任务,每隔3秒重复执行

4、提交固定延迟任务

executor.scheduleWithFixedDelay(new Task("fixed-delay"), 2, 3, TimeUnit.SECONDS); // 在2秒后开始执行任务,每次执行完毕后等待3秒再执行下一次

5、关闭线程池

executor.shutdown(); // 优雅关闭线程池,等待所有任务完成
executor.shutdownNow(); // 强制关闭线程池,停止所有任务

定时线程池的应用场景

1、定时任务调度

场景描述:需要定期执行某些任务,比如每天凌晨清理日志、每小时统计系统数据等。

实现方式:使用 scheduleAtFixedRatescheduleWithFixedDelay 方法,设置任务的执行间隔。

示例

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(() -> {
    System.out.println("执行每日数据备份任务: " + new Date());
}, 0, 24, TimeUnit.HOURS); // 每隔24小时执行一次

2、缓存过期清理

场景描述:在缓存系统中,需要定期清理过期的缓存数据。

实现方式:使用定时线程池定期扫描缓存,清理过期的数据。

示例

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(() -> {
    cache.cleanExpiredEntries(); // 清理过期缓存
    System.out.println("缓存清理完成: " + new Date());
}, 0, 1, TimeUnit.HOURS); // 每隔1小时清理一次

3、心跳检测

场景描述:在分布式系统中,需要定期向其他服务发送心跳包,检测服务是否存活。

实现方式:使用定时线程池定期发送心跳请求。

示例

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(() -> {
    boolean isAlive = heartBeatCheck(); // 发送心跳检测
    if (!isAlive) {
        System.out.println("服务不可用,触发告警!");
    }
}, 0, 10, TimeUnit.SECONDS); // 每隔10秒检测一次

4、延迟任务执行

场景描述:某些任务需要延迟一段时间后执行,比如订单超时未支付自动取消。

实现方式:使用 schedule 方法,设置任务的延迟时间。

示例

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.schedule(() -> {
    orderService.cancelOrder(orderId); // 取消订单
    System.out.println("订单已取消: " + orderId);
}, 30, TimeUnit.MINUTES); // 延迟30分钟执行

定时线程池scheduleAtFixedRate与scheduleWithFixedDelay区别

image-20250205224359033

scheduleAtFixedRate案例demo(period,任务之间间隔)

说明:scheduleAtFixedRate 方法:第三个参数是period,表示每间隔period时间执行一次任务(如果period为2s,但是前一个任务为3s,此时会在前一个任务执行完后再执行)。

public class ScheduledThreadPoolExecutorDemo {
    public static void main(String[] args) {
				test01();
    }

    // scheduleAtFixedRate 方法:第三个参数是period,表示每间隔period时间执行一次任务(如果period为2s,但是前一个任务为3s,此时会在前一个任务执行完后再执行)
    public static void test01() {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        // 第0s开始执行,每间隔2s执行一次任务
        executor.scheduleAtFixedRate(() -> {
            LocalDateTime startTime = LocalDateTime.now();
            System.out.println("Task started at: " + startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + ", thread: " + Thread.currentThread().getName());
            try {
                Thread.sleep(3000); // 模拟任务耗时3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LocalDateTime endTime = LocalDateTime.now();
            System.out.println("Task finished at: " + endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }, 0, 2, TimeUnit.SECONDS);
    }

}

效果如下:

image-20250205224710125

scheduleWithFixedDelay案例demo(delay,上一个任务结束后间隔时间)

说明:scheduleWithFixedDelay:第三个参数为delay,指的是上一个任务执行完之后间隔delay来执行任务

public class ScheduledThreadPoolExecutorDemo {
    public static void main(String[] args) {
        test02();
    }
    // scheduleWithFixedDelay:第三个参数为delay,指的是上一个任务执行完之后间隔delay来执行任务
    public static void test02() {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleWithFixedDelay(() -> {
            LocalDateTime startTime = LocalDateTime.now();
            System.out.println("Task started at: " + startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + ", thread: " + Thread.currentThread().getName());
            try {
                Thread.sleep(3000); // 模拟任务耗时3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LocalDateTime endTime = LocalDateTime.now();
            System.out.println("Task finished at: " + endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }, 0, 2, TimeUnit.SECONDS);
    }
}

效果如下:

image-20250205225001345

ScheduledThreadPoolExecutor源码

如何实现队列任务的捞取(即任务的调度和执行)?

认识DelayedWorkQueue

DelayedWorkQueue是一个基于优先队列的无界阻塞队列,用于存储ScheduledFutureTask对象。它按照任务的触发时间排序,确保最早到期的任务优先出队。

关键方法

  • poll:从队列中取出最早到期的任务。
  • offer:将任务添加到队列中。
  • take:阻塞等待,直到队列中有任务到期。

任务的封装(ScheduledFutureTask)ScheduledFutureTaskScheduledThreadPoolExecutor中用于封装任务的类。它继承自FutureTask,并添加了与调度相关的逻辑。

关键字段

  • sequenceNumber:任务的序列号,用于解决触发时间相同的情况。
  • period:任务的周期(对于scheduleAtFixedRate)或延迟(对于scheduleWithFixedDelay)。
  • time:任务的下次触发时间。

关键方法

  • run:执行任务逻辑。
  • setNextRunTime:设置任务的下次触发时间。

理解DelayedWorkQueue#take

核心任务调度:ScheduledThreadPoolExecutor的工作线程会不断地从DelayedWorkQueue中捞取任务并执行。

DelayedWorkQueuetake方法

public ScheduledFutureTask<?> take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (;;) {
            // 获取当前时间
            long currentTime = now();
            // 获取队列头部的任务
            ScheduledFutureTask<?> first = queue.peek();
            if (first == null) {
                // 如果队列为空,等待
                available.await();
            } else {
                // 如果任务已经到期
                if (first.isPeriodic() && first.getPeriod() < 0) {
                    // 周期性任务,重新计算下次触发时间
                    long period = first.getPeriod();
                    long nextTime = first.getNextExecutionTime();
                    first.setNextExecutionTime(nextTime + period);
                    first.setPeriod(period);
                }
                if (first.getDelay(NANOSECONDS) <= 0) {
                    // 任务到期,移除并返回
                    return queue.poll();
                }
                // 如果任务未到期,等待
                long delay = first.getDelay(NANOSECONDS);
                if (leader == null) {
                    leader = Thread.currentThread();
                    available.awaitNanos(delay);
                    leader = null;
                } else {
                    available.awaitNanos(delay);
                }
            }
        }
    } finally {
        lock.unlock();
    }
}

捞取和执行流程

捞取和执行流程如下:

  1. 任务添加到队列
    • 当调用scheduleAtFixedRatescheduleWithFixedDelay时,任务被封装为ScheduledFutureTask对象,并添加到DelayedWorkQueue中。
    • DelayedWorkQueue会按照任务的触发时间排序。
  2. 工作线程捞取任务
    • 工作线程通过调用DelayedWorkQueuetake方法来获取最早到期的任务。
    • 如果任务尚未到期,工作线程会阻塞等待。
  3. 任务执行
    • 工作线程获取任务后,调用任务的run方法来执行任务。
    • 如果任务是周期性任务(如scheduleAtFixedRate),任务执行完成后会重新计算下次触发时间,并再次添加到队列中。

参考文章

[1]. Java并发包线程池之ScheduledThreadPoolExecutor:https://www.cnblogs.com/txmfz/p/11222873.html


资料获取

大家点赞、收藏、关注、评论啦~

精彩专栏推荐订阅:在下方专栏👇🏻

  • 长路-文章目录汇总(算法、后端Java、前端、运维技术导航):博主所有博客导航索引汇总
  • 开源项目Studio-Vue—校园工作室管理系统(含前后台,SpringBoot+Vue):博主个人独立项目,包含详细部署上线视频,已开源
  • 学习与生活-专栏:可以了解博主的学习历程
  • 算法专栏:算法收录

更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅


整理者:长路 时间:2025.2.5

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在Mac mini M4上部署DeepSeek R1本地大模型

在Mac mini M4上部署DeepSeek R1本地大模型 安装ollama 本地部署&#xff0c;我们可以通过Ollama来进行安装 Ollama 官方版&#xff1a;【点击前往】 Web UI 控制端【点击安装】 如何在MacOS上更换Ollama的模型位置 默认安装时&#xff0c;OLLAMA_MODELS 位置在"~/.o…

动态规划练习九(完全背包问题)

一、问题介绍与解题心得 完全背包问题与01背包问题很相似&#xff0c;不同点就是每个物品数量有多个&#xff0c;每个物品可以取多个或不取&#xff0c;来达到收益最大&#xff0c;或者收益在某个值。 限制条件&#xff1a;背包容量有限 解决问题&#xff1a;从价值入手&…

百亿大表的实时分析:华安基金 HTAP 数据库的选型历程与 TiDB 使用体验

导读 在金融科技迅猛发展的今天&#xff0c;华安基金作为行业的先行者&#xff0c;面临着数据管理和分析的全新挑战。随着业务的不断扩展和数据量的激增&#xff0c;传统的数据库架构已难以满足系统对实时性、灵活性和分析能力的需求。在这样的背景下&#xff0c;HTAP&#xf…

低代码系统-产品架构案例介绍、蓝凌(十三)

蓝凌低代码系统&#xff0c;依旧是从下到上&#xff0c;从左至右的顺序。 技术平台h/iPaas 指低层使用了哪些技术&#xff0c;例如&#xff1a;微服务架构&#xff0c;MySql数据库。个人认为&#xff0c;如果是市场的主流&#xff0c;就没必要赘述了。 新一代门户 门户设计器&a…

DeepSeek研究员在线爆料:R1训练仅用两到三周,春节期间观察到R1 zero强大进化

内容提要 刚刚我注意到DeepSeek研究员Daya Guo回复了网友有关DeepSeek R1的一些问题&#xff0c;以及接下来的公司的计划&#xff0c;只能说DeepSeek的R1仅仅只是开始&#xff0c;内部研究还在快速推进&#xff0c;DeepSeek 的研究员过年都没歇&#xff0c;一直在爆肝推进研究…

【Rust自学】20.1. 最后的项目:单线程Web服务器

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 20.1.1. 什么是TCP和HTTP Web 服务器涉及的两个主要协议是超文本传输​​协议(Hypertext T…

19.[前端开发]Day19-王者荣项目耀实战(二)

01_(掌握)王者荣耀-main-banner展示实现 完整代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…

Java 基于微信小程序的高校失物招领平台小程序(附源码,文档)

博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战*✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447…

题解:洛谷 P5837 [USACO19DEC] Milk Pumping G

题目https://www.luogu.com.cn/problem/P5837 温馨提示&#xff1a;鉴于数据范围小的可怜&#xff0c;我们可以用暴力一些的想法去做&#xff0c;别看到是普及/提高就被吓退了。 枚举最小流量 &#xff0c;然后跑一遍最短路&#xff0c;求出带限制的 到 的最短路的长度&#…

动态规划——斐波那契数列模型问题

文章目录 1137. 第 N 个泰波那契数算法原理代码实现 面试题 08.01. 三步问题算法原理代码实现 746. 使用最小花费爬楼梯算法原理代码实现 91. 解码方法算法原理代码实现 1137. 第 N 个泰波那契数 题目链接&#xff1a;1137. 第 N 个泰波那契数 算法原理 状态表示&#xff1a;…

LabVIEW涡轮诊断系统

一、项目背景与行业痛点 涡轮机械是发电厂、航空发动机、石油化工等领域的核心动力设备&#xff0c;其运行状态直接关系到生产安全与经济效益。据统计&#xff0c;涡轮故障导致的非计划停机可造成每小时数十万元的经济损失&#xff0c;且突发故障可能引发严重安全事故。传统人…

java程序员面试自身优缺点,详细说明

程序员面试大厂经常被问到的Java异常机制问题,你搞懂了吗运行时异常:运行时异常是可能被程序员避免的异常。与检查性相反,运行时异常可以在编译时被忽略。错误(ERROR):错误不是异常,而是脱离程序员控制的问题。错误通常在代码中容易被忽略。例如:当栈溢出时,一个错误就发生了,它…

大话特征工程:3.特征扩展

公元 2147 年&#xff0c;人类文明站在科技的巅峰&#xff0c;所有决策、发展甚至感知都被“全维计算网络”所掌控。这套系统以高维空间中的数据为基础&#xff0c;试图预测并塑造未来。然而&#xff0c;这场辉煌的技术革命却在悄无声息之间酿成了人类最大的危机——维数灾难。…

CSV数据分析智能工具(基于OpenAI API和streamlit)

utils.py&#xff1a; from langchain_openai import ChatOpenAI from langchain_experimental.agents.agent_toolkits import create_csv_agent import jsonPROMPT_TEMPLATE """你是一位数据分析助手&#xff0c;你的回应内容取决于用户的请求内容。1. 对于文…

2025.2.5

Web [SWPUCTF 2021 新生赛]ez_unserialize: 这个题先了解一下反序列化&#xff1a;反序列化是序列化的逆过程。序列化是将对象或数据结构转换为可以存储或传输的格式&#xff08;如JSON、XML或二进制格式&#xff09;的过程。反序列化则是将这个格式的数据转换回原始的对象或…

新版AndroidStudio 修改 jdk版本

一、问题 之前&#xff0c;在安卓项目中配置JDK和Gradle的过程非常直观&#xff0c;只需要进入Android Studio的File菜单中的Project Structure即可进行设置&#xff0c;十分方便。 如下图可以在这修改JDK: 但是升级AndroidStudio之后&#xff0c;比如我升级到了Android Stu…

Web3技术详解

Web3技术代表着互联网技术的最新进展&#xff0c;它致力于打造一个去中心化的互联网生态系统。以下是对Web3技术的详细解析&#xff1a; 一、Web3技术的核心概念 Web3是第三代互联网技术的代名词&#xff0c;代表着去中心化、区块链驱动和用户自有控制的理念。在Web3的世界中…

景联文科技:专业数据采集标注公司 ,助力企业提升算法精度!

随着人工智能技术加速落地&#xff0c;高质量数据已成为驱动AI模型训练与优化的核心资源。据统计&#xff0c;全球AI数据服务市场规模预计2025年突破200亿美元&#xff0c;其中智能家居、智慧交通、医疗健康等数据需求占比超60%。作为国内领先的AI数据服务商&#xff0c;景联文…

3.【BUUCTF】XSS-Lab1

进入题目页面如下 好好好&#xff0c;提示点击图片&#xff0c;点进去页面如下&#xff0c;且url中有传参&#xff0c;有注入点 发现题目给出了源码 查看得到本题的源码 分析一下代码 <!DOCTYPE html><!--STATUS OK--> <!-- 声明文档类型为 HTML5&#xff0c;告…

进程、线程、内存和IO模型的概念详解

进程、线程、内存和IO模型的概念详解 1 进程与线程1.1 进程1.1.1 进程分类1.1.2 进程的状态和转换1.1.3 僵尸进程和孤儿进程的区别1.1.4 进程之间的通信1.1.5 用户态和内核态1.1.6 用户空间和内核空间 1.2 线程1.2.1 线程的状态和转换1.2.2 进程与线程的区别 1.3 多进程和多线程…