ScheduledThreadPoolExecutor 使用线程池执行定时任务
转自:https://segmentfault.com/a/1190000008038848
在现实世界里,我们总是免不了要定期去做一件事情(比如上课)—— 在计算机的世界里,更是如此。比如我们手机每天叫我们起床的电子闹钟,某些网站会定期向我们发送一些推荐相关的邮件,集群中我们需要每隔一定时间检查是否有机器宕机等。
在 使用线程池 中已经介绍,JDK 1.5 时,标准类库添加了对线程池的支持,然后在线程池核心实现 ThreadPoolExecutor 的基础上,实现了 ScheduledThreadPoolExecutor,作为可以 定时和周期性执行任务 的线程池。ScheduledThreadPoolExecutor 的类图如下:
ScheduledThreadPoolExecutor 实现了 ScheduledExecutorService 接口,ScheduledExecutorService 继承了 ExecutorService 接口,所以首先 ScheduledThreadPoolExecutor 是一个 ExecutorService (线程池),然后除了具有线程池的功能,它还有定时和周期性执行任务的功能。ScheduledExecutorService 除了从 ExecutorService 继承的方法外,还包括如下四个方法:
第一个 Schedule 方法:
delay 指定的时间后,执行指定的 Runnable 任务,可以通过返回的 ScheduledFuture<?> 与该任务进行交互。
第二个 Schedule 方法:
delay 指定的时间后,执行指定的 Callable<V> 任务,可以通过返回的 ScheduledFuture<V> 与该任务进行交互。
(ScheduledFuture 接口 继承自 Future 接口,所以 ScheduledFuture 和任务的交互方式与 Future 一致。所以通过ScheduledFuture,可以 判断定时任务是否已经完成,获得定时任务的返回值,或者取消任务等)
scheduleAtFixedRate 方法:
initialDelay 指定的时间后,开始按周期 period 执行指定的 Runnable 任务。
假设调用该方法后的时间点为 0,那么第一次执行任务的时间点为 initialDelay,第二次为 initialDelay + period,第三次为 initialDelay + period + period,以此类推。
scheduleWithFixedDelay 方法:
initialDelay 指定的时间后,开始按指定的 delay 延期性的执行指定的 Runnable 任务。
假设调用该方法后的时间点为 0,每次任务需要耗时 T(i)(i 为第几次执行任务),那么第一次执行任务的时间点为 initialDelay,第一次完成任务的时间点为 initialDelay + T(1),则第二次执行任务的时间点为 initialDelay + T(1) + delay;第二次完成任务的时间点为 initialDelay + (T(1) + delay) + T(2),所以第三次执行任务的时间点为 initialDelay + T(1) + delay + T(2) + delay,以此类推。
我们来实践下 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate 方法:
public class ScheduledExecutorServiceTest {
public static void main(String[] args) throws Exception {
ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
TimerTask timerTask = new TimerTask(2000); // 任务需要 2000 ms 才能执行完毕
System.out.printf("起始时间:%s\n\n", new SimpleDateFormat("HH:mm:ss").format(new Date()));
// 延时 1 秒后,按 3 秒的周期执行任务
timer.scheduleAtFixedRate(timerTask, 1000, 3000, TimeUnit.MILLISECONDS);
}
private static class TimerTask implements Runnable {
private final int sleepTime;
private final SimpleDateFormat dateFormat;
public TimerTask(int sleepTime) {
this.sleepTime = sleepTime;
dateFormat = new SimpleDateFormat("HH:mm:ss");
}
@Override
public void run() {
System.out.println("任务开始,当前时间:" + dateFormat.format(new Date()));
try {
System.out.println("模拟任务运行...");
Thread.sleep(sleepTime);
} catch (InterruptedException ex) {
ex.printStackTrace(System.err);
}
System.out.println("任务结束,当前时间:" + dateFormat.format(new Date()));
System.out.println();
}
}
}
运行结果:

可以看到运行结果完全符合预期 —— 延时 1 秒后,每隔 3 秒执行一次任务。
上面是任务的运行时间小于周期时间的情况 —— 那如果任务运行的时间大于给定的执行周期呢?(比如任务运行需要 3 s,但是我们指定的周期为 2 s)
修改 main 方法:
public static void main(String[] args) throws Exception {
ScheduledExecutorService timer = Executors.newScheduledThreadPool(2);
TimerTask timerTask = new TimerTask(3000); // 每个任务需要 3000 ms 才能执行完毕
System.out.printf("起始时间:%s\n\n", new SimpleDateFormat("HH:mm:ss").format(new Date()));
timer.scheduleAtFixedRate(timerTask, 1000, 2000, TimeUnit.MILLISECONDS);
}
运行结果:

可以看到此时虽然我们指定的周期为 2 s,但是因为任务的运行就需要 3 s(超过周期),所以这种情况下 scheduleAtFixedRate 的处理方式为 上一次任务刚完成,则紧接着立即运行下一次任务,而不是使用线程池中的空闲线程来运行任务以维护 2 秒这个周期 —— 由此可见,每个定时任务在 ScheduledThreadPoolExecutor 中,都是串行运行的,即下一次运行任务一定在上一次任务结束之后。
(scheduleWithFixedDelay 方法 的使用也十分简单,请有兴趣的读者自己实践)
ScheduledThreadPoolExecutor 使用线程池执行定时任务的更多相关文章
- Spring中的线程池和定时任务功能
1.功能介绍 Spring框架提供了线程池和定时任务执行的抽象接口:TaskExecutor和TaskScheduler来支持异步执行任务和定时执行任务功能.同时使用框架自己定义的抽象接口来屏蔽掉底层 ...
- ThreadPoolExecutor源码分析-面试问烂了的Java线程池执行流程,如果要问你具体的执行细节,你还会吗?
Java版本:8u261. 对于Java中的线程池,面试问的最多的就是线程池中各个参数的含义,又或者是线程池执行的流程,彷佛这已成为了固定的模式与套路.但是假如我是面试官,现在我想问一些更细致的问题, ...
- 捕获Java线程池执行任务抛出的异常
捕获Java线程池执行任务抛出的异常Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常, public interface Runnable { publi ...
- Java 使用线程池执行若干任务
在执行一系列带有IO操作(例如下载文件),且互不相关的异步任务时,采用多线程可以很极大的提高运行效率.线程池包含了一系列的线程,并且可以管理这些线程.例如:创建线程,销毁线程等.本文将介绍如何使用Ja ...
- JAVA中线程池启动定时任务
/** * 用线程池启动定时器 * @param args */ public static void main(String[] args) { //10秒启动 Executors.newSched ...
- 9、java5线程池之定时任务线程池newScheduledThreadPool与newSingleThreadScheduledExecutor
JDK文档描述 newSingleThreadScheduledExecutor() 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行.(注意,如果因为在关闭前的执行期间出现失败而终 ...
- spring boot使用自定义配置的线程池执行Async异步任务
一.增加配置属性类 package com.chhliu.springboot.async.configuration; import org.springframework.boot.context ...
- java 多线程 线程池:多核CPU利用ExecutorService newWorkStealingPool; ForkJoinPool线程池 执行可拆分的任务RecursiveAction;RecursiveTask
1,给定并行级别: 1,ExecutorService newWorkStealingPool(int parallelism): 创建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队 ...
- 对象回收过程?线程池执行过程? map原理?集合类关系?synchronized 和 volatile ? 同一个类的方法事务传播控制还有作用吗?java 锁
1. 对象回收过程? 可达性分析算法: 如果一个对象从 GC Roots 不可达时,则证明此对象不可用. 通过一系列称为GC ROOTS的对象作为起点,从这些起点往下搜索,搜索走过的路径 称为引用链 ...
随机推荐
- PAT 乙级 1008 数组元素循环右移问题 (20) C++版
1008. 数组元素循环右移问题 (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 一个数组A中存有N(N>0)个整数,在不允 ...
- 方法 - ShellCode测试
机器码->汇编 1 // ShellCode测试.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <windo ...
- 阿里云ECS安装的redis服务器,用java代码去连接报错。
import redis.clients.jedis.Jedis; /** * Hello world! * */ public class App { public static void main ...
- SCCM 2012 R2实战系列之八:OSD(上)--分发全新Windows7系统
今天将跟大家一起分享SCCM 中最为重要的一个功能---操作系统分发(OSD),在此文章中会讨论到OSD的初始化配置.镜像的导入.任务序列的创建编辑.并解决大家经常遇到的分发windows7系统分区盘 ...
- front-end architecture
这些东西都需要管理,并且提供一种比较好的方案去维护.在JavaScript被模块化之后,也可以通过单元测试来控制它们的质量,并且把这个过程自动化,每次版本有变更之前,保证它们最基本的正确性.最终,需要 ...
- 由于ip改变重新配置CM集群
修改所有主机/etc/hosts 修改所有agent节点的/opt/cm-5.5.1/etc/cloudera-scm-agent/config.ini,中server的ip 主节点启动cm serv ...
- 第7章 网络层协议(2)_ICMP协议
2. ICMP协议 2.1 ICMP报文(Internet Control Message Protocol)的类型 报文类型 类型值 代码 描述 请求报文 8 0 请求回显报文 响应报文 0 0 回 ...
- mysql 取整数或小数或精确位数
select cast(3.1415926 as decimal(9,2))精确到几位 select round(1024.5); 四舍五入 select floor(1024.5);取整数部分 se ...
- phpstudy远程连接mysql
格局如图所示执行以下命令 mysql -u root -p mysql>use mysql; mysql>select 'host' from user where user='root' ...
- RecyclerView拖拽排序;
效果就是这样,RecyclerView列表可拖拽排序,可删除,可添加: RecyclerView给我们提供了一个手势器: ItemTouchHelper helper = new ItemTouchH ...