定时/延时任务-ScheduledThreadPoolExecutor的使用

news2024/11/30 5:21:37

文章目录

  • 1. 概要
  • 2. 固定速率和固定延时
    • 2.1 固定速率
    • 2.2 固定延时
  • 3. API 解释
    • 3.1 schedule
    • 3.2 固定延时 - scheduleWithFixedDelay
    • 3.2 固定速率 - scheduleWithFixedDelay
  • 4. 小结

1. 概要

前三篇文章的地址:

  1. 定时/延时任务-自己实现一个简单的定时器
  2. 定时/延时任务-Timer用法
  3. 定时/延时任务-细说Timer源码

ScheduledThreadPoolExecutor Java 并发包 (java.util.concurrent) 中的一个类,同时也是 ThreadPoolExecutor 的一个子类,这就意味者 ScheduledThreadPoolExecutor 不像 Timer 中使用单个线程去执行任务,ScheduledThreadPoolExecutor 使用了线程池去执行,同时 ScheduledThreadPoolExecutor 也具备了 Timer 中的各种功能。

2. 固定速率和固定延时

2.1 固定速率

固定速率 策略表示任务在固定的时间间隔内重复执行,不管任务的执行时间有多长,如果任务的执行时间超过了时间间隔,那么下一个任务会在当前任务执行完毕之后就会马上开始执行,下面是一个例子:假设我们设置了一个固定速率为 5 的任务,从 0s 开始执行,也就是说这个任务 5s 执行一次:

  • 第一次执行:0s 开始执行一次,假设执行时间是 3s
  • 第二次执行:5s 开始执行第二次,假设执行的时候被阻塞了,执行了 8s
  • 第三次执行:13s 开始执行第三次,假设执行的时候被阻塞了,执行了 3s
  • 第四次执行:16s 开始执行第四次,假设执行的时候没有被阻塞
  • 第四次执行:20s 开始执行第五次,假设执行的时候没有被阻塞


2.2 固定延时

固定延时 策略表示任务在当前任务执行完成之后,固定延时一段时间再执行下一个任务,下面是一个例子:假设我们设置了一个固定速率为 5 的任务,从 0s 开始执行,也就是说这个任务 5s 执行一次:

  • 第一次执行:0s 开始执行一次,假设执行时间是 3s
  • 第二次执行:8s 开始执行第二次,假设执行的时候被阻塞了,执行了 8s
  • 第三次执行:21s 开始执行第三次,假设执行的时候被阻塞了,执行了 3s
  • 第四次执行:29 开始执行第四次,假设执行的时候没有被阻塞
  • 第四次执行:34s 开始执行第五次,假设执行的时候没有被阻塞

上面的例子中其实固定速率的执行和 Timer 是一样的,但是固定延时有点不一样,不知道你有没有发现,固定延时下执行的时候每一个任务和前一个任务的时间间隔一定是 任务执行时间 + 延时时间,其实相比于 TimerScheduledThreadPoolExecutor 的固定延时看起来更像是 “正宗” 一点的固定延时


3. API 解释

3.1 schedule

还是老规矩,先看下 ScheduledThreadPoolExecutor 的几个 API,看看用法是什么样的,首先就是 schedule 方法,这个方法就是普通的延时方法,只执行一次,非周期调度

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);
        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());
                if (Math.random() < 0.5) {
                    System.out.println("sleep: 3s");
                    Thread.sleep(3000);
                } else {
                    System.out.println("sleep: 8s");
                    Thread.sleep(8000);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-executor-run");
        executor.schedule(thread, 0, TimeUnit.SECONDS);
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

执行结果如下所示,延迟 0s 就开始执行任务
在这里插入图片描述
ScheduledThreadPoolExecutor 也提供了一个 callable 类型的任务的实现,使用 Callable 的好处就是可以获取任务的实现返回值,如下例子所示:

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);

        // 创建一个 Callable 任务
        Callable<Integer> task = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("Task is running on thread: " + Thread.currentThread().getName());
                return 1; // 返回结果
            }
        };

        // 调度 Callable 任务,在 0 秒后执行
        ScheduledFuture<Integer> future = executor.schedule(task, 0, TimeUnit.SECONDS);
        try {
            // 获取任务的结果
            Integer result = future.get();
            System.out.println("Task result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

输出结果如下所示:
在这里插入图片描述

3.2 固定延时 - scheduleWithFixedDelay

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);

        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());
                if (Math.random() < 0.5) {
                    System.out.println("sleep: 3s");
                    Thread.sleep(3000);
                } else {
                    System.out.println("sleep: 8s");
                    Thread.sleep(8000);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-executor-run");

        executor.scheduleWithFixedDelay(thread, 0, 5, TimeUnit.SECONDS);
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

输入如下所示:
在这里插入图片描述
来分析一下:

  • 首先第一次任务 2024-11-24 19:19:36:698 启动,然后执行时间 3s
  • 第二次任务在 2024-11-24 19:19:44:718 启动,跟第一次任务相差 8s,延时 = 5s + 3s = 8s
  • 第三次任务在 2024-11-24 19:19:52:741 启动,跟第二次任务相差 8s,延时 = 5s + 3s = 8s
  • 第四次任务在 2024-11-24 19:20:05:760 启动,跟第三次任务相差 13s,延时 = 5s + 8s = 13s
  • 第五次任务在 2024-11-24 19:20:13:773 启动,跟第四次任务相差 8s,延时 = 5s + 3s = 8s
  • 第六次任务在 2024-11-24 19:20:26:787 启动,跟第五次任务相差 13s,延时 = 5s + 8s = 13s

可以看到 scheduleWithFixedDelay 的延时是相对于当前时间 + 延时时间的,跟 Timer 的固定延时任务有点不同

3.2 固定速率 - scheduleWithFixedDelay

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);

        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());
                if (Math.random() < 0.5) {
                    System.out.println("sleep: 3s");
                    Thread.sleep(3000);
                } else {
                    System.out.println("sleep: 8s");
                    Thread.sleep(8000);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-executor-run");

        executor.scheduleAtFixedRate(thread, 0, 5, TimeUnit.SECONDS);
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

输出结果如下:
在这里插入图片描述
来分析下上面的执行流程,需要注意的是,ScheduledThreadPoolExecutor 的固定速率任务会在当前任务执行完之后再添加下一次要执行的任务

  • 首先第一次任务 2024-11-24 16:23:25:762 启动,然后执行时间 8s,执行完之后会添加一个 2024-11-24 16:23:30 s 执行的任务,当前时间 2024-11-24 16:23:33 s
  • 第二次任务在 2024-11-24 16:23:33:770 启动,因为 2024-11-24 16:23:33:770 超过了 2024-11-24 16:23:30 s 这个时间点, 所以立刻执行,执行时间 3s,执行完之后往队列里面添加一个 2024-11-24 16:23:35 s 执行的任务,当前时间 2024-11-24 16:23:36 s
  • 第二次任务在 2024-11-24 16:23:36:784 启动,因为 2024-11-24 16:23:36:784 超过了 2024-11-24 16:23:35 s 这个时间点, 所以立刻执行,执行时间 3s,执行完之后往队列里面添加一个 2024-11-24 16:23:40 s 执行的任务,当前时间 2024-11-24 16:23:39 s
  • 第四次任务在 2024-11-24 16:23:40:739 启动,因为第三个任务执行完之后时间是 2024-11-24 16:23:39 s,还没有到第四个任务执行的时间点,这时候就等待,等到 2024-11-24 16:23:40:739,执行时间 3s,执行完之后往队列里面添加一个 2024-11-24 16:23:45 s 执行的任务,执行完的时间当前是 2024-11-24 16:23:42 s
  • 第五次任务在 2024-11-24 16:23:45:751 启动,因为第四个任务执行完之后时间是 2024-11-24 16:23:42 s,还没有到第五个任务执行的时间点,这时候就等待,等到 2024-11-24 16:23:45:751,执行时间 8s,执行完之后往队列里面添加一个 2024-11-24 16:23:50 s 执行的任务,执行完的时间当前是 2024-11-24 16:23:53 s
  • 第六次任务在 2024-11-24 16:23:53:763 启动,因为第六个任务执行时间是 2024-11-24 16:23:50 s,而执行完第五个任务已经是 2024-11-24 16:23:53 s了,超过任务的执行时间,所以会立马执行,执行时间 3s,执行完后往队列里面添加一个 2024-11-24 16:23:55 s 的任务,当前时间是 2024-11-24 16:23:56 s

其实上面的解释中可以把后面的毫秒去掉,这样比较方便理解

4. 小结

上面就是几个 API 了,其中固定速率的和 Timer 的实现方式有点不同,后续文章中,我会对 ScheduledThreadPoolExecutor 的源码进行详细的解析,当然在解析 ScheduledThreadPoolExecutor 之前首先需要知道下线程池的工作原理和工作流程,所以下一篇文章,我会说下线程池的工作原理

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

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

相关文章

什么是sfp,onu,​为什么PON(​俗称“光猫”​)模块使用SC光纤接口

在现代网络设备中&#xff0c;我们经常会看到SFP或SFP接口的身影&#xff0c;这些接口有时被简称为光口&#xff0c;但这个称呼并不严谨。有些厂商则称之为多功能口或多用途口&#xff0c;然而这对于不了解的人来说可能还是一头雾水。SFP&#xff0c;即Small Form-Factor Plugg…

005 MATLAB符号微积分

前言&#xff1a; 在MATLAB中&#xff0c;数值与符号的主要区别在于它们的处理方式和应用场景 数值计算适用于实际的数值计算问题&#xff0c;如矩阵运算、数据分析等。符号计算适用于符号推导、公式化简和符号解析&#xff0c;如理论物理和工程计算。 01 符号对象 1.基本符…

深入实践:从零开始掌握GPT的应用开发

1. 为什么选择GPT&#xff1f; GPT&#xff08;Generative Pre-trained Transformer&#xff09;是当下最具影响力的语言生成模型之一&#xff0c;适用于生成文本、分析语言情感、翻译、多任务对话等多种场景。相比传统算法和模型&#xff0c;GPT有以下显著优势&#xff1a; …

WRF-Chem模式安装、环境配置、原理、调试、运行方法;数据准备及相关参数设置方法

大气污染是工农业生产、生活、交通、城市化等方面人为活动的综合结果&#xff0c;同时气象因素是控制大气污染的关键自然因素。大气污染问题既是局部、当地的&#xff0c;也是区域的&#xff0c;甚至是全球的。本地的污染物排放除了对当地造成严重影响外&#xff0c;同时还会在…

开源项目:纯Python构建的中后台管理系统

来源&#xff1a;Python大数据分析 费弗里 大家好我是费老师&#xff0c;目前市面上有很多开源的「中后台管理系统」解决方案&#xff0c;复杂如「若依」那种前端基于Vue&#xff0c;后端基于Java的框架&#xff0c;虽然其提供了较为完善的一整套前后端分离权限管理系统解决方…

汽车免拆诊断案例 | 2017款捷豹F-PACE车发动机偶尔怠速不稳

故障现象  一辆2017款捷豹F-PACE车&#xff0c;搭载2.0 L GTDi发动机&#xff0c;累计行驶里程约为16万km。车主反映&#xff0c;车辆组合仪表上发动机故障灯点亮&#xff08;图1&#xff09;&#xff0c;且发动机偶尔怠速不稳。 图1 发动机故障灯点亮 故障诊断 接车后试车…

SQL进阶技巧:非等值连接--单向近距离匹配

目录 0 场景描述 1 数据准备 2 问题分析 ​编辑 ​编辑 3 小结 数字化建设通关指南 0 场景描述 表 t_1 和表 t_2 通过 a 和 b 关联时&#xff0c;有相等的取相等的值匹配&#xff0c;不相等时每一 个 a 的值在 b 中找差值最小的来匹。 表 t_1&#xff1a;a 中无重复值…

泷羽sec-云技术

基础之云技术 声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec…

element ui select绑定的值是对象的属性时,显示异常.

需要声明 value-key"value",如果还不行可能是数据类型不一致数字0和字符串0是不一致的. el-select v-model"value" clearable placeholder"Select" value-key"value" style"width: 240px"><!-- <el-option v-for&…

【ChatGPT大模型开发调用】如何获得 OpenAl API Key?

如何获取 OpenAI API Key 获取 OpenAI API Key 主要有以下三种途径&#xff1a; OpenAI 官方平台 (推荐): 开发者用户可以直接在 OpenAI 官方网站 (platform.openai.com) 注册并申请 API Key。 通常&#xff0c;您可以在账户设置或开发者平台的相关页面找到申请入口。 Azure…

沸点 | 嬴图Powerhouse全面发布:从用户视角看嬴图实时图数据库的成长与价值

嬴图Powerhouse&#xff0c;直译过来就是能量站、动力站&#xff0c;它是嬴图自2019年发布高密度并行图计算引擎以来的一个里程碑&#xff0c;包括对整个产品架构的一个彻底革新&#xff0c;为大量复杂数据进行计算、分析和存储提供了快速和强大的动力支持。目前嬴图的用户正在…

深入理解计算机系统,源码到可执行文件翻译过程:预处理、编译,汇编和链接

1.前言 从一个高级语言到可执行程序&#xff0c;要经过预处理、编译&#xff0c;汇编和链接四个过程。大家可以思考下&#xff0c;为什么要有这样的过程&#xff1f; 我们学习计算机之处&#xff0c;就应该了解到&#xff0c;计算机能够识别的只有二进制语言&#xff08;这是…

Gitee markdown 使用方法(持续更新)

IPKISS 获取仿真器件的名称 引言正文标题换行第一种------在行末尾手动键入两个空格第二种------额外换行一次&#xff0c;即两行中间留一个空行 缩进与反缩进代码块行内代码添加图片添加超链接 加粗&#xff0c;倾斜&#xff0c;加粗倾斜 引言 有些保密性的文件或者教程&…

Element UI 打包探索【1】

目录 第一个命令 第二个命令 node build/bin/iconInit.js node build/bin/build-entry.js node build/bin/i18n.js node build/bin/version.js 总结 最近在接触组件库的项目&#xff0c;所以特意拿来Element UI借鉴学习一下&#xff0c;它算是做前端的同学们离不开的一…

关于IDE的相关知识之一【使用技巧】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于ide使用技巧的相关内容&#xff01; 关于…

【C语言】字符串字面量的特殊性

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;字符串字面量的定义和存储位置&#x1f4af;字符串字面量的不可修改性&#x1f4af;字符数组与字符串字面量的区别字符数组的定义和可修改性指针与数组的区别 &#x1…

linux(redhat8)如何安装mysql8.0之rpmtar双版本(最新版)(内网)(离线)

一.环境 系统版本&#xff1a;Red Hat 8.5.0-20 Java环境&#xff1a;build 1.8.0_181-b13 MYSQL&#xff1a;8.x版本 二、查看内核版本 #查看内核版本&#xff0c;根据内核版本下载对应的安装包 cat /proc/version 三、安装方式 一、rpm包方式 一、下载安装包 1. 登录网…

Python 中 if __name__ == ‘__main__‘ 有什么作用 ?

Python 以其简单性和可读性而闻名&#xff0c;但是它包含了一小段代码 &#xff1a;if name ‘main’: 经常让新手感到困惑。 理解 name 属性 为了理解 if name ‘main’: 的重要性&#xff0c;我们首先需要理解 name 在 Python 中的含义。Python 中的每个模块都有一个名为…

djinn:1 靶场学习小记

一、测试环境&#xff1a; kail攻击机&#xff1a;Get Kali | Kali Linux 靶场镜像&#xff1a;https://download.vulnhub.com/djinn/djinn.ova 描述&#xff1a; 该机器与 VirtualBox 和 VMWare 兼容。DHCP 将自动分配一个 IP。您将在登录屏幕上看到 IP。您必须找到并读取分…

外卖点餐系统小程序

目录 开发前准备 项目展示项目分析项目初始化封装网络请求 任务1 商家首页 任务分析焦点图切换中间区域单击跳转到菜单列表底部商品展示 任务2 菜单列表 任务分析折扣信息区设计菜单列表布局请求数据实现菜单栏联动单品列表功能 任务3 购物车 任务分析设计底部购物车区域添加商…