ScheduledThreadPoolExecutor学习

简介

ScheduledThreadPoolExecutor 是 Java 中的一个类,它属于 java.util.concurrent 包。这个类是一个线程池,用于在给定的延迟后运行命令,或者定期地执行命令。它是 ThreadPoolExecutor 的一个子类,专门用于处理需要定时或周期性执行的任务。

ScheduledThreadPoolExecutor 的主要特点如下:

  • 线程池大小固定:线程池的大小在创建时就已经设定,并且之后不会改变。
  • 任务调度:它可以用来调度一次性任务,也可以用来调度重复执行的任务。
  • 延迟执行:任务可以在给定的延迟之后开始执行。
  • 周期性执行:任务可以周期性地执行,例如每隔固定时间执行一次。

ScheduledThreadPoolExecutor 提供的主要方法包括:

  • schedule(Runnable command, long delay, TimeUnit unit): 在给定的延迟后执行一次任务。
  • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit): 以固定的速率周期性地执行任务。
  • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit): 在给定的初始延迟后首次执行任务,然后每次任务执行完毕之后等待指定的延迟再次执行。

源码

public class ScheduledThreadPoolExecutorextends ThreadPoolExecutorimplements ScheduledExecutorService {public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit) {//任务或单位为空 则抛出异常if (command == null || unit == null)throw new NullPointerException();//把要执行的任务包装成 RunnableScheduledFutureRunnableScheduledFuture<?> t = decorateTask(command,new ScheduledFutureTask<Void>(command, null,triggerTime(delay, unit)));//延时执行定时任务delayedExecute(t);return t;}//逻辑同上public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit) {if (callable == null || unit == null)throw new NullPointerException();RunnableScheduledFuture<V> t = decorateTask(callable,new ScheduledFutureTask<V>(callable,triggerTime(delay, unit)));delayedExecute(t);return t;}public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) {//任务和单位为空则抛出异常if (command == null || unit == null)throw new NullPointerException();if (period <= 0)//执行周期时间小于等于0 则抛出异常throw new IllegalArgumentException();//将任务包装成ScheduledFutureTaskScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(period));//然后包装成RunnableScheduledFutureRunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;//延时执行任务delayedExecute(t);return t;}//同上public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {if (command == null || unit == null)throw new NullPointerException();if (delay <= 0)throw new IllegalArgumentException();ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(-delay));RunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;delayedExecute(t);return t;}//所谓的包装就是直接返回RunnableScheduledFuture对象protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {return task;}//从scheduleWithFixedDelay方法的源代码,我们可以看出在将Runnable对象封装成ScheduledFutureTask时,设置了执行周期,//但是此时设置的执行周期与scheduleAtFixedRate方法设置的执行周期不同。//此时设置的执行周期规则为:下一次任务执行的时间是上一次任务完成的时间加上delay时长,时长单位由TimeUnit决定。//也就是说,具体的执行时间不是固定的,但是执行的周期是固定的,整体采用的是相对固定的延迟来执行定时任务public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {//传入的Runnable对象和TimeUnit为空,则抛出空指针异常if (command == null || unit == null)throw new NullPointerException();//任务延时时长小于或者等于0,则抛出非法参数异常if (delay <= 0)throw new IllegalArgumentException();//将Runnable对象封装成ScheduledFutureTask任务//并设置固定的执行周期来执行任务ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(-delay));//调用decorateTask方法,本质上直接返回ScheduledFutureTask任务RunnableScheduledFuture<Void> t = decorateTask(command, sft);//设置执行的任务sft.outerTask = t;//执行延时任务delayedExecute(t);return t;}private void setNextRunTime() {//距离下次执行任务的时长long p = period;//固定频率执行,//上次执行任务的时间//加上任务的执行周期if (p > 0)time += p;//相对固定的延迟//使用的是系统当前时间//加上任务的执行周期elsetime = triggerTime(-p);}//这两个triggerTime方法的代码比较简单,就是获取下一次执行任务的具体时间。//有一点需要注意的是:delay <(Long.MAX_VALUE >> 1判断delay的值是否小于Long.MAX_VALUE的一半,//如果小于Long.MAX_VALUE值的一半,则直接返回delay,否则需要处理溢出的情况。private long triggerTime(long delay, TimeUnit unit) {return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));}long triggerTime(long delay) {return now() +((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));}private long overflowFree(long delay) {//获取队列中的节点Delayed head = (Delayed) super.getQueue().peek();//获取的节点不为空,则进行后续处理if (head != null) {//从队列节点中获取延迟时间long headDelay = head.getDelay(NANOSECONDS);//如果从队列中获取的延迟时间小于0,并且传递的delay//值减去从队列节点中获取延迟时间小于0if (headDelay < 0 && (delay - headDelay < 0))//将delay的值设置为Long.MAX_VALUE + headDelaydelay = Long.MAX_VALUE + headDelay;}//返回延迟时间return delay;}private void delayedExecute(RunnableScheduledFuture<?> task) {//如果当前线程池已经关闭//则执行线程池的拒绝策略if (isShutdown())reject(task);//线程池没有关闭else {//将任务添加到阻塞队列中super.getQueue().add(task);//如果当前线程池是SHUTDOWN状态//并且当前线程池状态下不能执行任务//并且成功从阻塞队列中移除任务if (isShutdown() &&!canRunInCurrentRunState(task.isPeriodic()) &&remove(task))//取消任务的执行,但不会中断执行中的任务task.cancel(false);else//调用ThreadPoolExecutor类中的ensurePrestart()方法ensurePrestart();}}void reExecutePeriodic(RunnableScheduledFuture<?> task) {//线程池当前状态下能够执行任务if (canRunInCurrentRunState(true)) {//将任务放入队列super.getQueue().add(task);//线程池当前状态下不能执行任务,并且成功移除任务if (!canRunInCurrentRunState(true) && remove(task))//取消任务task.cancel(false);else//调用ThreadPoolExecutor类的ensurePrestart()方法ensurePrestart();}}//ScheduledThreadPoolExecutor类中的onShutdown方法的主要逻辑就是先判断线程池调用shutdown方法后,是否继续执行现有的延迟任务和定时任务,//如果不再执行,则取消任务并清空队列;如果继续执行,将队列中的任务强转为RunnableScheduledFuture对象之后,从队列中删除并取消任务。//大家需要好好理解这两种处理方式。最后调用ThreadPoolExecutor类的tryTerminate方法。@Overridevoid onShutdown() {//获取队列BlockingQueue<Runnable> q = super.getQueue();//在线程池已经调用shutdown方法后,是否继续执行现有延迟任务boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy();//在线程池已经调用shutdown方法后,是否继续执行现有定时任务boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy();//在线程池已经调用shutdown方法后,不继续执行现有延迟任务和定时任务if (!keepDelayed && !keepPeriodic) {//遍历队列中的所有任务for (Object e : q.toArray())//取消任务的执行if (e instanceof RunnableScheduledFuture<?>)((RunnableScheduledFuture<?>) e).cancel(false);//清空队列q.clear();}//在线程池已经调用shutdown方法后,继续执行现有延迟任务和定时任务else {//遍历队列中的所有任务for (Object e : q.toArray()) {//当前任务是RunnableScheduledFuture类型if (e instanceof RunnableScheduledFuture) {//将任务强转为RunnableScheduledFuture类型RunnableScheduledFuture<?> t = (RunnableScheduledFuture<?>) e;//在线程池调用shutdown方法后不继续的延迟任务或周期任务//则从队列中删除并取消任务if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||t.isCancelled()) {if (q.remove(t))t.cancel(false);}}}}//最终调用tryTerminate()方法tryTerminate();}}

示例

import java.util.concurrent.Executors;  
import java.util.concurrent.ScheduledThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  public class ScheduledThreadPoolExecutorExample {  public static void main(String[] args) {  // 创建一个包含单个线程的ScheduledThreadPoolExecutor  ScheduledThreadPoolExecutor executor = Executors.newScheduledThreadPool(1);  // 创建一个Runnable任务  Runnable task = () -> System.out.println("Task is running: " + System.currentTimeMillis());  // 在10秒后执行该任务  executor.schedule(task, 10, TimeUnit.SECONDS);  // 每隔5秒执行一次该任务  executor.scheduleAtFixedRate(task, 0, 5, TimeUnit.SECONDS);  // 在初始延迟15秒后执行,之后每次任务执行完等待2秒再次执行  executor.scheduleWithFixedDelay(task, 15, 2, TimeUnit.SECONDS);  // 注意:在实际应用中,你应该适当地关闭线程池以避免资源泄露  // executor.shutdown(); // 这将平滑地关闭线程池,等待所有任务完成  }  
}

在这个例子中,我们创建了一个 ScheduledThreadPoolExecutor 实例,它包含单个线程。我们定义了三个不同的任务调度:一个在10秒后执行,一个每隔5秒执行一次,还有一个在初始延迟15秒后执行,然后每次任务执行完毕之后等待2秒再次执行。

请注意,在实际应用中,当你不再需要 ScheduledThreadPoolExecutor 时,应该调用 shutdown 或 shutdownNow 方法来关闭线程池,以避免资源泄露。同时,线程池中的任务也应该设计为能够在适当的时候结束执行,避免无限期地占用资源。

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

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

相关文章

解释索引是什么以及它们是如何提高查询性能的

索引在数据库管理系统中是一个重要的数据结构&#xff0c;用于帮助快速检索数据库表中的数据。它可以被看作是一个指向表中数据的指针列表&#xff0c;这些指针按照某种特定的顺序&#xff08;如字母顺序或数字顺序&#xff09;排列。索引的工作原理类似于书籍的目录&#xff1…

Python爬虫实战第二例【二】

零.前言&#xff1a; 本文章借鉴&#xff1a;Python爬虫实战&#xff08;五&#xff09;&#xff1a;根据关键字爬取某度图片批量下载到本地&#xff08;附上完整源码&#xff09;_python爬虫下载图片-CSDN博客 大佬的文章里面有API的获取&#xff0c;在这里我就不赘述了。 一…

kitex 入门和基于grpc的使用

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于kitex系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系…

【Web】青少年CTF擂台挑战赛 2024 #Round 1 wp

好家伙&#xff0c;比赛结束了还有一道0解web题是吧( 随缘写点wp(简单过头&#xff0c;看个乐就好) 目录 EasyMD5 PHP的后门 PHP的XXE Easy_SQLi 雏形系统 EasyMD5 进来是个文件上传界面 说是只能上传pdf&#xff0c;那就改Content-Type为application/pdf&#xff0c;改…

11.盛最多水的容器

题目&#xff1a;给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 解题思路&#xff1a;可以…

判断闰年(1000-2000)

判断规则&#xff1a;1.能被4整除&#xff0c;不能被100整除是闰年,2.能被400整除是闰年 #include <stdio.h>int is_leap_year(int n){if((n % 400 0)||((n % 4 0)&&(n % 100 ! 0)))return 1;elsereturn 0; } int main() {int i 0;int count 0;for(i 1000;…

基于PHP的在线英语学习平台

有需要请加文章底部Q哦 可远程调试 基于PHP的在线英语学习平台 一 介绍 此在线英语学习平台基于原生PHP开发&#xff0c;数据库mysql。系统角色分为学生&#xff0c;教师和管理员。(附带参考设计文档) 技术栈&#xff1a;phpmysqlphpstudyvscode 二 功能 学生 1 注册/登录/…

C++/Python简单练手题

前言 最近需要开始使用python&#xff0c;但是对python了解的并不多&#xff0c;于是先从很早之前刚学C时写过的一些练手题开始&#xff0c;使用python来实现相同的功能&#xff0c;在温习python基础语法的同时&#xff0c;也一起来感受感受python的魅力 99乘法表 c&#xf…

kettle开发-Day43-加密环境下运行作业

前言&#xff1a; 金三银四&#xff0c;开年第一篇我们来介绍下&#xff0c;怎么在加密情况下运行我们的kettle作业及任务。无疑现在所有企业都认识到加密的重要性&#xff0c;加密后的文件在对外传输的时候不能被访问&#xff0c;访问时出现一堆乱码&#xff0c;同时正常的应用…

1分钟学会Python字符串前后缀与编解码

1.前缀和后缀 前缀和后缀指的是&#xff1a;字符串是否以指定字符开头和结尾 2.startswith() 判断字符串是否以指定字符开头&#xff0c;若是返回True&#xff0c;若不是返回False str1 "HelloPython"print(str1.startswith("Hello")) # Trueprint…

Navicat Premium 16:打破数据库界限,实现高效管理mac/win版

Navicat Premium 16是一款功能强大的数据库管理工具&#xff0c;旨在帮助用户更轻松地连接、管理和保护各种数据库。该软件支持多种数据库系统&#xff0c;如MySQL、Oracle、SQL Server、PostgreSQL等&#xff0c;并提供了直观的图形界面&#xff0c;使用户能够轻松地完成各种数…

【力扣白嫖日记】585.2016年的投资

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 585.2016年的投资 表&#xff1a;Person 列名类型pidinttiv_2015floattiv_2016floatlatfloatlonfloat pid …

AI也来打掼蛋,难道人工智能也能当领导?

在人工智能&#xff08;AI&#xff09;的研究领域中&#xff0c;游戏被视为现实世界的简化模型&#xff0c;常常是研究的首选平台。这些研究主要关注游戏代理的决策过程。例如&#xff0c;中国的传统卡牌游戏“掼蛋”&#xff08;字面意思是“扔鸡蛋”&#xff09;就是一个挑战…

Unity(第十七部)Unity自带的角色控制器

组件Character Controller 中文角色控制器 using System.Collections; using System.Collections.Generic; using UnityEngine;public class player : MonoBehaviour {private CharacterController player;void Start(){player GetComponent<CharacterController>();}v…

对于爬虫的学习

本地爬取 package MyApi.a08regexdemo;import java.util.regex.Matcher; import java.util.regex.Pattern;public class RegexDemo03 {public static void main(String[] args) {//要求&#xff1a;找出里面所有javaxxString str"Java自从95年问世以来&#xff0c;经历了…

腾讯日常实习-数据科学-初试凉经

个人背景&#xff1a;双985 腾讯会议面了一个小时左右&#xff0c;过程如下&#xff1a; 1.面试官首先介绍了一下部门&#xff08;腾讯云&#xff09;的情况和业务方向。 2.让我介绍一下自己&#xff08;目前情况&#xff0c;科研经历&#xff0c;项目经历&#xff09;。 3.就我…

HarmonyOS—编译构建概述

编译构建是将应用/服务的源代码、资源、第三方库等&#xff0c;通过编译工具转换为可直接在硬件设备上运行的二进制机器码&#xff0c;然后再将二进制机器码封装为HAP/APP软件包&#xff0c;并为HAP/APP包进行签名的过程。其中&#xff0c;HAP是可以直接运行在模拟器或真机设备…

牛皮癣发作和复发的触发因素

谷禾健康 银屑病&#xff0c;又叫牛皮癣&#xff0c;会导致出现皮疹伴发痒的鳞状斑块&#xff0c;最常见于膝盖、肘部、躯干和头皮。通常呈周期性发展&#xff0c;发作数周或数月&#xff0c;然后消退一段时间&#xff0c;长期的发作和复发会给患者带来很大的痛苦和困扰&#x…

Qt5.9.9交叉编译(带sqlite3、OpenSSL)

1、交叉编译工具链 这里ARM平台是ARM CortexA9的&#xff0c;一般交叉编译工具链demo板厂商都会提供&#xff0c;若未提供或想更换新版本的交叉编译工具链可参考以下方式获取。 1.1 下载适用于ARM CortexA9的交叉编译工具链 Linaro Releases下载gcc4的最新版xxxx-i686_arm-li…

洛谷P1009阶乘之和

题目描述 用高精度计算出S1!2!3!⋯n!&#xff08;n≤50&#xff09;。 其中 ! 表示阶乘&#xff0c;定义为 n!n(n−1)(n−2)⋯1。例如&#xff0c;5!543211205!54321120。 输入格式 一个正整数 n。 输出格式 一个正整数 S&#xff0c;表示计算结果。 输入输出样例 输入…