深入理解高并发编程 - 深度解析ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor 并实现了 ScheduledExecutorService 接口,这使得它可以同时充当线程池和定时任务调度器。

构造方法

public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}

构造方法接收一个 corePoolSize 参数,它表示线程池中的核心线程数。核心线程是一直保持存活的线程,即使没有任务执行,以便支持定时任务的调度。

构造方法的参数及其含义如下:

corePoolSize:线程池中的核心线程数。这些核心线程会一直保持存活,即使没有任务需要执行。核心线程数决定了线程池的基本能力,可以处理的并发任务数为核心线程数。Integer.MAX_VALUE:线程池的最大线程数。这里设置为整数的最大值,表示线程池没有最大线程数限制。当任务数量超过核心线程数时,新的任务会被排队到队列中等待执行。0:非核心线程的闲置存活时间。对于非核心线程(超过核心线程数的线程),如果没有任务需要执行,它们在闲置一段时间后会被回收。NANOSECONDS:时间单位,表示使用纳秒作为时间单位。new DelayedWorkQueue():构造一个 DelayedWorkQueue 对象作为任务队列。DelayedWorkQueue 是一个延迟队列,用于存储定时任务,根据任务的调度时间进行排序。

在这个构造方法中,ScheduledThreadPoolExecutor 将核心线程数、最大线程数、任务队列等参数传递给其父类 ThreadPoolExecutor 的构造方法,从而创建一个具有定时任务调度能力的线程池。

schedule 方法

ScheduledThreadPoolExecutor 提供了一系列 schedule 方法,用于调度定时任务的执行。这些方法允许指定任务的延迟时间,任务会在延迟时间过后执行一次。以下是 schedule 方法的签名和解析:

schedule(Runnable command, long delay, TimeUnit unit):

这个方法用于调度一个 Runnable 任务,在指定的延迟时间后执行一次。返回一个 ScheduledFuture 对象,可以使用它来取消任务或者获取任务执行的结果。

command:要执行的任务。
delay:延迟时间。
unit:时间单位。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("Task executed after delay");
ScheduledFuture<?> future = scheduler.schedule(task, 5, TimeUnit.SECONDS);
<V> schedule(Callable<V> callable, long delay, TimeUnit unit):

这个方法与上一个方法类似,不同之处在于它可以调度一个带有返回值的 Callable 任务。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Callable<String> task = () -> "Task result";
ScheduledFuture<String> future = scheduler.schedule(task, 3, TimeUnit.SECONDS);
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):

这个方法用于调度一个 Runnable 任务,在初始延迟时间后开始执行,然后每隔一定的时间间隔执行一次。任务的执行时间不考虑,每次任务会尽量在指定的时间间隔后执行。

command:要执行的任务。
initialDelay:初始延迟时间。
period:时间间隔。
unit:时间单位。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("Task executed at fixed rate");
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):

这个方法与上一个方法类似,不同之处在于任务的执行间隔是基于任务完成的时间,而不是固定的时间间隔。任务完成后,会等待一段时间(delay)再执行下一次。

command:要执行的任务。
initialDelay:初始延迟时间。
delay:间隔时间。
unit:时间单位。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("Task executed with fixed delay");
ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(task, 2, 3, TimeUnit.SECONDS);

这些 schedule 方法可以方便地调度不同类型的定时任务,从简单的延迟执行到周期性执行,以满足各种需求。

decorateTask 方法

在 ScheduledThreadPoolExecutor 中,decorateTask 方法用于对任务进行修饰,以支持定时任务的调度。它会将普通的 Runnable 或 Callable 任务包装成 ScheduledFutureTask 对象,这样可以将任务调度相关的信息添加到任务中,如延迟时间、调度时间等。以下是 decorateTask 方法的部分实现:

protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {return task;
}protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) {return task;
}

这两个方法分别用于对 Runnable 和 Callable 任务进行修饰。它们的作用是将传入的任务(runnable 或 callable)与现有的 RunnableScheduledFuture 任务对象进行合并,从而创建一个新的 RunnableScheduledFuture 对象。

decorateTask 方法默认的实现是将传入的任务不做任何修饰,直接返回原来的任务对象。这是因为大部分情况下,任务并不需要额外的修饰。但是,也可以在自定义的子类中覆盖这个方法,以实现自己的修饰逻辑。

这个方法的存在使得 ScheduledThreadPoolExecutor 可以在任务执行前对任务对象进行一些预处理操作,以满足不同的调度需求。通常情况下,不需要直接调用这个方法,它会在 schedule 方法内部自动调用。

scheduleAtFixedRate 方法

scheduleAtFixedRate 方法用于调度一个任务,在初始延迟时间后开始执行,然后每隔一定的时间间隔执行一次。任务的执行时间不考虑,每次任务会尽量在指定的时间间隔后执行。以下是 scheduleAtFixedRate 方法的签名和解析:

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

参数说明:

command:要执行的任务,即一个实现了 Runnable 接口的对象。
initialDelay:初始延迟时间,表示从调度开始到第一次执行的时间间隔。
period:任务执行的周期间隔。
unit:时间单位。
返回值:ScheduledFuture 对象,表示任务的调度结果。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);Runnable task = () -> System.out.println("Task executed at fixed rate");// 初始延迟 1 秒,周期间隔 2 秒
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);

在上述示例中,任务 task 首次会在 1 秒后执行,然后每隔 2 秒执行一次。需要注意的是,scheduleAtFixedRate 方法不会考虑任务的执行时间。如果任务的执行时间超过指定的周期间隔,后续任务会尽量在每个周期的开始时执行,但可能会累积延迟。因此,如果任务的执行时间很长,可能会影响后续任务的调度,导致调度的间隔变得不精确。

总之,scheduleAtFixedRate 方法适用于需要以固定的时间间隔调度任务,而不考虑任务的执行时间。

scheduleWithFixedDelay 方法

scheduleWithFixedDelay 方法用于调度一个任务,在初始延迟时间后开始执行,然后每次任务完成后会等待一段时间,再执行下一次。任务的执行间隔是基于任务完成的时间,而不是固定的时间间隔。以下是 scheduleWithFixedDelay 方法的签名和解析:

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

参数说明:

command:要执行的任务,即一个实现了 Runnable 接口的对象。
initialDelay:初始延迟时间,表示从调度开始到第一次执行的时间间隔。
delay:每次任务执行后的等待时间,即任务完成后等待多长时间再执行下一次。
unit:时间单位。
返回值:ScheduledFuture 对象,表示任务的调度结果。

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);Runnable task = () -> System.out.println("Task executed with fixed delay");// 初始延迟 2 秒,每次任务执行后等待 3 秒再执行
ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(task, 2, 3, TimeUnit.SECONDS);

在上述示例中,任务 task 首次会在 2 秒后执行,然后每次任务完成后会等待 3 秒,再执行下一次。这个方法适用于需要在任务执行完毕后等待一段时间再进行下一次调度的情况,可以避免任务的执行时间对后续任务的调度造成影响。

需要注意的是,scheduleWithFixedDelay 方法会考虑任务的执行时间,确保每次任务的执行间隔都是基于上一次任务完成的时间。这样可以保证在任何情况下,任务之间的时间间隔都保持相对稳定。

triggerTime 方法

onShutdown 方法用于在线程池关闭时执行特定的清理操作。以下是 onShutdown 方法的签名和解析:

protected void onShutdown() {// 默认的实现为空
}

这个方法是一个钩子方法(hook method),在线程池执行 shutdown 或 shutdownNow 方法时会被调用。默认情况下,onShutdown 方法没有实际的实现,即空方法。但是可以通过继承 ScheduledThreadPoolExecutor 并覆盖 onShutdown 方法,来自定义在线程池关闭时的操作。

在自定义实现中,可以进行一些资源释放、清理工作,或者记录日志等操作,以确保线程池的正常关闭和资源的释放。例如:

class CustomScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {

public CustomScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize);
}@Override
protected void onShutdown() {// 在线程池关闭时执行特定的清理操作System.out.println("CustomThreadPoolExecutor is shutting down...");// 执行其他清理操作...super.onShutdown();
}

}
通过覆盖 onShutdown 方法,可以在线程池关闭时执行自定义的清理逻辑,从而确保应用程序的资源管理和退出行为。

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

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

相关文章

Redis、Memcache和MongoDB的区别

>>Memcached Memcached的优点&#xff1a; Memcached可以利用多核优势&#xff0c;单实例吞吐量极高&#xff0c;可以达到几十万QPS&#xff08;取决于key、value的字节大小以及服务器硬件性能&#xff0c;日常环境中QPS高峰大约在4-6w左右&#xff09;。适用于最大程度…

图数据库_Neo4j_Centos7.9安装Neo4j社区版3.5.4_基于jdk1.8---Neo4j图数据库工作笔记0011

首先上传安装包,到opt/soft目录 然后看一下jdk安装的是什么版本的,因为在neo4j 4以后就必须要用jdk11 以上的版本,我这里还用着jdk1.8 所以 我这里用3.5.4的版本 关于下载地址: https://dist.neo4j.org/neo4j-community-3.5.4-unix.tar.gz 然后再去解压到/opt/module目录下 …

【目标检测】目标检测 相关学习笔记

目标检测算法 PASCALVOC2012数据集 挑战赛主要分为 图像分类 目标检测 目标分割 动作识别 数据集分为四个大类 交通&#xff08;飞机 船 公交车 摩托车&#xff09; 住房&#xff08;杯子 椅子 餐桌 沙发&#xff09; 动物&#xff08;鸟 猫 奶牛 狗 马 羊&#xff09; 其他&a…

2023国赛数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

[JavaWeb]【四】web后端开发-SpringBootWeb入门

目录 一 Spring 二 SpringBootWeb入门 2.1 入门需求 2.2 分析 2.3 开始创建SpringBootWeb 2.4 创建类实现需求 2.5 启动程序 2.6 访问 三 HTTP协议 3.1 HTTP-概述 3.2 HTTP-请求协议 3.3 HTTP-响应协议 3.3.1 响应状态码 && 响应类型 3.4 HTTP-协议解析 前言…

八大排序算法 - Java实现

冒泡排序 排序原理&#xff1a; 比较相邻的元素。如果前一个元素比后一个元素大&#xff0c;就交换这两个元素的位置。对每一对相邻元素做同样的工作&#xff0c;从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值 代码实现&#xff1a; import java.uti…

Liunx系统编程:进程信号的概念及产生方式

目录 一. 进程信号概述 1.1 生活中的信号 1.2 进程信号 1.3 信号的查看 二. 信号发送的本质 三. 信号产生的四种方式 3.1 按键产生信号 3.2 通过系统接口发送信号 3.2.1 kill -- 向指定进程发送信号 3.2.2 raise -- 当自身发送信号 3.2.3 abort -- 向自身发送进程终止…

【ES5和ES6】数组遍历的各种方法集合

一、ES5的方法 1.for循环 let arr [1, 2, 3] for (let i 0; i < arr.length; i) {console.log(arr[i]) } // 1 // 2 // 32.forEach() 特点&#xff1a; 没有返回值&#xff0c;只是针对每个元素调用func三个参数&#xff1a;item, index, arr &#xff1b;当前项&#…

嵌入式Linux开发实操(八):UART串口开发

串口可以说是非常好用的一个接口,它同USB、CAN、I2C、SPI等接口一样,为SOC/MCU构建了丰富的接口功能。那么在嵌入式linux中又是如何搭建和使用UART接口的呢? 一、Console接口即ttyS0 ttyS0通常做为u-boot(bootloader的一种,像是Windows的BIOS),它需要一个交互界面,一般…

HTML中的字符串转义

为什么要转义&#xff1f; 转义可以防止 xss 攻击。接下来&#xff0c;我们来看一下如何转义。 HTML Sanitizer API Sanitizer 是浏览器自带的转义方法&#xff0c;在2021年初被提出&#xff0c;兼容性问题很大。 列举几个常用的 API&#xff1a; const $div document.qu…

C++------利用C++实现二叉搜索树【数据结构】

文章目录 二叉搜索树概念二叉搜索树的操作查找插入删除 二叉搜索树的应用 二叉搜索树 概念 什么是二叉搜索树&#xff0c;二叉搜索树就是指左孩子永远比根小右孩子永远比根大。这个规则适用于所有的子树。 上面的就是一棵二叉搜索树&#xff0c;我们还可以发现这棵树走一个中…

英伟达结构化剪枝工具Nvidia Apex Automatic Sparsity [ASP](1)——使用方法

英伟达结构化剪枝工具Nvidia Apex Automatic Sparsity [ASP]&#xff08;1&#xff09;——使用方法 Apex是Nvdia维护的pytorch工具库&#xff0c;包括混合精度训练和分布式训练&#xff0c;Apex的目的是为了让用户能够更早的使用上这些“新鲜出炉”的训练工具。ASP&#xff0…

块设备驱动模板

内核版本&#xff1a;4.14.0 基于设备树 使用请求队列&#xff0c;请求队列会用到I/O调度器&#xff0c;适合机械硬盘这种存储设备。 #include <linux/module.h> #include <linux/blkdev.h> #include <linux/hdreg.h> #define RAMDISK_SIZE (2*1024*1024…

Windows安装 Elasticsearch 教程

下载地址 Past Releases of Elastic Stack Software | Elastic 解压 解压完的样子 进入BIN目录 D:\Develop\elasticsearch\elasticsearch-7.12.0\bin 按住shift 鼠标右键 打开 powershell 窗口 查看ES版本 .\elasticsearch.bat --version 出现问题了 警告&#xff1a;不赞成…

如何学习专业的学术用语01

问题的提出——凭啥人家写的词汇这么专业 做法一 做法二&#xff1a;做一个专业数据库 专门做教育技术类的

React(6)

1.React插槽 import React, { Component } from react import Child from ./compoent/Childexport default class App extends Component {render() {return (<div><Child><div>App下的div</div></Child></div>)} }import React, { Compon…

(二)结构型模式:4、组合模式(Composite Pattern)(C++实例)

目录 1、组合模式&#xff08;Composite Pattern&#xff09;含义 2、组合模式应用场景 3、组合模式的优缺点 4、组合模式的UML图学习 5、C实现组合模式的简单示例&#xff08;公司的OA系统&#xff09; 1、组合模式&#xff08;Composite Pattern&#xff09;含义 组合模…

2022寒假牛客训练4

G-子序列权值乘积 设计知识&#xff1a; 欧拉降幂 如果我们要求ab%p 而b是一个很大的数&#xff0c;可以先将b对p-1取模&#xff0c;不会影响结果的正确性&#xff0c;前提是a和p互质。 这一题我们可以将数字先进行排序&#xff0c;因为子序列中只有最大和最小的才会被计算&am…

Hyper-V 扩展虚拟磁盘后,如何扩容到 /ubuntu--vg-ubuntu--lv

创建虚拟机的时候&#xff0c;一般选择 动态扩展虚拟硬盘&#xff0c;N 多年以来&#xff0c;一直没有关心过他是如何动态扩展的&#xff0c;直到最近折腾大文件 SQL 导入任务&#xff0c;遇到了磁盘空间占满的情形 以下这 2 个就体现了动态扩展空间&#xff0c;扩展起来很容易…

Visual Studio 如何放大代码字体的大小

1.打开Visual Studio&#xff0c;新建一个程序&#xff0c;一段代码&#xff0c;为接下去的操作做好准备。单击菜单栏的【工具】选项。 2.在跳出来菜单中找到【选项】&#xff08;一般在最后一项&#xff09;&#xff0c;然后单击。跳出新的窗口。 3.跳出新的窗口后&#xff…