Executors框架之ScheduledExecutorService实现定时任务
一、简介
An ExecutorService that can schedule commands to run after a given delay, or to execute periodically.
(ExecutorService可以安排命令在给定的延迟后运行或定期执行。)
The schedule methods create tasks with various delays and return a task object that can be used to cancel or check execution. The scheduleAtFixedRate and scheduleWithFixedDelay methods create and execute tasks that run periodically until cancelled.
(调度方法会创建具有各种延迟的任务,并返回可用于取消或检查执行的任务对象。 scheduleAtFixedRate和scheduleWithFixedDelay方法创建并执行定期运行的任务,直到被取消为止。
)
Commands submitted using the Executor.execute(java.lang.Runnable) and ExecutorService submit methods are scheduled with a requested delay of zero. Zero and negative delays (but not periods) are also allowed in schedule methods, and are treated as requests for immediate execution.
(使用Executor.execute(java.lang.Runnable)和ExecutorService提交方法提交的命令的计划延迟为零。调度方法中还允许零延迟和负延迟(但不允许使用周期),并将其视为立即执行的请求。
)
All schedule methods accept relative delays and periods as arguments, not absolute times or dates. It is a simple matter to transform an absolute time represented as a Date to the required form. For example, to schedule at a certain future date, you can use: schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS). Beware however that expiration of a relative delay need not coincide with the current Date at which the task is enabled due to network time synchronization protocols, clock drift, or other factors. The Executors class provides convenient factory methods for the ScheduledExecutorService implementations provided in this package.
(所有调度方法都接受相对延迟和周期作为参数,而不是绝对时间或日期作为参数。将代表日期的绝对时间转换为所需的形式很简单。例如,要计划在某个将来的日期进行计划,可以使用:schedule(task,date.getTime()-System.currentTimeMillis(),TimeUnit.MILLISECONDS)。但是请注意,由于网络时间同步协议,时钟漂移或其他因素,相对延迟的到期时间不必与启用任务的当前日期一致。 Executors类为此程序包中提供的ScheduledExecutorService实现提供了方便的工厂方法。
)
(jdk7 doc , translate by google )
二、两个常用定时任务
1. 固定周期的定时任务
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
创建并执行一个周期性操作,该操作将在给定的初始延迟后首先启用,然后在给定的时间段内启用;即后执行将开始 在initialDelay然后在initialDelay +周期,然后 在initialDelay + 2 *周期,依此类推。
2. 固定延迟的定时任务
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建并执行一个周期性操作,该操作将在给定的初始延迟后首先启用,然后在一个执行的终止与下一个执行的开始之间具有给定的延迟。
3. 一次性延迟任务 Runnable
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
创建并执行一次操作,该操作在给定的延迟后变为启用状态。
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
System.out.println("一次性的延迟任务, 10S 后执行");
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("一次性延迟任务");
}
}, 10L, SECONDS);
4. 一次性延迟任务 Callable
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
创建并执行ScheduledFuture,该ScheduledFuture在给定的延迟后变为启用状态。
三、示例代码
class PrintControl {
private final SimpleDateFormat SDF = new SimpleDateFormat("hh:mm:ss");
/**
* @param int corePoolSize 线程池中最小的线程数, 无任务时保持, 任务过多时可以超出这个值
*/
private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
/**
* 给定延迟的打印任务
* 以固定的延迟时间(delay)去执行任务
*/
public void printForDelay() {
Runnable print = () -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ScheduledWithFixedDelay" + SDF.format(new Date()));
};
/**
* @param Runnable command
* @param long initialDelay
* @param long delay
* @param TimeUnit unit
*/
scheduled.scheduleWithFixedDelay(print, 0L, 5L, SECONDS);
}
/**
* 定期去执行打印任务
* 以固定的周期(period)去执行任务
*/
public void printForRate() {
Runnable print = () -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ScheduledAtFixedRate" + SDF.format(new Date()));
};
/**
* @param Runnable command
* @param long initialDelay
* @param long period
* @param TimeUnit unit
*/
scheduled.scheduleAtFixedRate(print, 0L, 5L, SECONDS);
}
}
四、通过ThreadFactory 指定任务线程名称
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
使用ThreadFactory设置明确的线程名称, 这样在调试的时候就可以很清晰的找到任务线程, 便于调试
参考 Executors.defaultThreadFactory()
class PrintControl {
private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1, ExecutorsefaultThreadFactory());
}
class PrintThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "PrintThreadFactory");
}
}
五、结束任务
1. 异常抛出
如果任务抛出异常会导致周期任务无法继续进行, 所以最好结合try cache处理.
此时线程不会结束.
final AtomicInteger count = new AtomicInteger(0);
ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1, new PrintThreadFactory());
StringBuilder nullPoint = null;
Runnable print = new Runnable() {
@Override
public void run() {
System.out.println("print " + count.getAndIncrement());
// print 4 之后, 任务不再继续执行
if (count.get() == 5) {
nullPoint.toString();
}
}
};
schedule.scheduleAtFixedRate(print, 0L, 2L, SECONDS);
2. 结束任务
Future.cancle(boolean mayInterruptIfRunning)
通过Debugger可以看出, 只是任务结束了,线程还在继续wait
// 最终打印 "print 3"之后打印"is canclelled", 任务不再继续, 线程不被关闭
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch countDownLatch = new CountDownLatch(1);
Future future = null;
ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1, new PrintThreadFactory());
Runnable print = new Runnable() {
@Override
public void run() {
System.out.println("print " + count.getAndIncrement());
if (count.get() == 3) {
countDownLatch.countDown();
}
}
};
future = schedule.scheduleAtFixedRate(print, 0L, 2L, SECONDS);
try {
countDownLatch.await();
future.cancel(true);
if (future.isCancelled()) {
System.out.println("is canclelled");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
3. 结束线程
3.1. void ExecutorService.shutdown();
Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
(启动有序关闭,在该关闭中执行先前提交的任务,但不接受任何新任务。)
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch countDownLatch = new CountDownLatch(1);
ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1, new PrintThreadFactory());
Runnable print = new Runnable() {
@Override
public void run() {
System.out.println("print " + count.getAndIncrement());
if (count.get() == 3) {
countDownLatch.countDown();
System.out.println("任务还在继续...");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务结束");
}
}
};
schedule.scheduleAtFixedRate(print, 0L, 2L, SECONDS);
try {
countDownLatch.await();
schedule.shutdown();
// schedule.shutdownNow();
if (schedule.isShutdown()) {
System.out.println("Schedule is shutdown");
}
// 阻塞
if (schedule.awaitTermination(10L, SECONDS)) {
System.out.println("termination");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
打印结果:
print 0
print 1
print 2
任务还在继续...
Schedule is shutdown
任务结束
termination
3.2. List ExecutorService.shutdownNow();
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.
(尝试停止所有正在执行的任务,暂停正在等待的任务的处理,并返回正在等待执行的任务的列表。)
final AtomicInteger count = new AtomicInteger(0);
final CountDownLatch countDownLatch = new CountDownLatch(1);
ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1, new PrintThreadFactory());
Runnable print = new Runnable() {
@Override
public void run() {
System.out.println("print " + count.getAndIncrement());
if (count.get() == 3) {
countDownLatch.countDown();
System.out.println("任务还在继续...");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务结束");
}
}
};
schedule.scheduleAtFixedRate(print, 0L, 2L, SECONDS);
try {
countDownLatch.await();
// schedule.shutdown();
schedule.shutdownNow();
if (schedule.isShutdown()) {
System.out.println("Schedule is shutdown");
}
// 阻塞
if (schedule.awaitTermination(10L, SECONDS)) {
System.out.println("termination");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
打印结果: 可以看出 Thread.sleep()发生了中断异常
print 0
print 1
print 2
任务还在继续...
Schedule is shutdown
java.lang.InterruptedException: sleep interrupted
...
任务结束
termination
3.3. boolean ExecutorService.awaitTermination(long timeout, TimeUnit unit)
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
(阻塞直到关闭请求后所有任务完成执行,或者发生超时,或者当前线程被中断(以先发生的为准)。)
六、参考
Executors框架之ScheduledExecutorService实现定时任务的更多相关文章
- javade多任务处理之Executors框架(线程池)实现的内置几种方式与两种基本自定义方式
一 Executors框架(线程池) 主要是解决开发人员进行线程的有效控制,原理可以看jdk源码,主要是由java.uitl.concurrent.ThreadPoolExecutor类实现的,这里只 ...
- 什么是 Executors 框架?
Executor 框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框 架. 无限制的创建线程会引起应用程序内存溢出.所以创建一个线程池是个更好的的 解决方案,因为可以限制线程的数量并且可以 ...
- java多线程系列:Executors框架
目录 Executor接口介绍 ExecutorService常用接口介绍 创建线程池的一些方法介绍 3.1 newFixedThreadPool方法 3.2 newCachedThreadPool方 ...
- 基于spring的quartz定时框架,实现简单的定时任务功能
在项目中,经常会用到定时任务,这就需要使用quartz框架去进行操作. 今天就把我最近做的个人主页项目里面的定时刷新功能分享一下,很简单. 首先需要配置一个配置文件,因为我是基于spring框架的,所 ...
- Java之ssh框架spring配置文件配置定时任务
最近做了一个数据同步功能,要求晚上0点去定时同步数据,这是个老项目框架用的ssh,定时任务基于quartz,废话不多说,下面详细说说相关配置. 在spring的配置文件中: <!-- 0点定时任 ...
- Spring框架中的Quartz定时任务使用笔记(通过@Scheduled注解的方式实现)
1.修改spring的xml配置信息 applicationContext.xml 三个部分内容 1.xmlns添加:xmlns:task="http://www.springframewo ...
- Spring框架最简单的定时任务调用
package org.jeecgframework.core.timer; import org.springframework.scheduling.annotation.Scheduled; i ...
- SpringMVC框架使用注解执行定时任务(转)
首先要配置我们的SpringMVC文件 xmlns 加下面的内容: xmlns:task="http://www.springframework.org/schema/task" ...
- <线程池-定时任务> ScheduledExecutorService之shutdown引发的RejectedExecutionException问题
一. 问题描述 先来看一下异常信息,启动tomcat时就报错: 2015-3-20 15:22:39 org.apache.catalina.core.StandardContext listener ...
随机推荐
- HttpServletRequest对象,请求行、请求头、请求体
HttpServletRequest 公共接口类HttpServletRequest继承自ServletRequest.客户端浏览器发出的请求被封装成为一个HttpServletRequest对象.对 ...
- Linux (三) 基础命令 上
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一 . 背景知识 1. Linux系统中一切皆文件 在Linux系统中任何东西都是以文件形式来存储的.这 ...
- Java实现 蓝桥杯VIP 算法提高 研究兔子的土豪
试题 算法提高 研究兔子的土豪 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 某天,HWD老师开始研究兔子,因为他是个土豪 ,所以他居然一下子买了一个可以容纳10^18代兔子的巨大 ...
- Java实现 LeetCode 48 旋转图像
48. 旋转图像 给定一个 n × n 的二维矩阵表示一个图像. 将图像顺时针旋转 90 度. 说明: 你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵.请不要使用另一个矩阵来旋转图像. 示 ...
- Java实现 蓝桥杯 素因子去重
素因子去重 问题描述 给定一个正整数n,求一个正整数p,满足p仅包含n的所有素因子,且每个素因子的次数不大于1 输入格式 一个整数,表示n 输出格式 输出一行,包含一个整数p. 样例输入 1000 样 ...
- linux性能监控工具nmon生成HTML报告-EasyNmon
一.关于easyNmon说明 为了方便多场景批量性能测试,用golang写了个监控程序,可以通过get url方式启动和停止nmon服务,非常适合配合Loadrunner性能测试框架和jmeter使用 ...
- pyhon的浅拷贝与深拷贝
1.赋值:简单地拷贝对象的引用,两个对象的id相同. 2.浅拷贝:创建一个新的组合对象,这个新对象与原对象共享内存中的子对象. 3.深拷贝:创建一个新的组合对象,同时递归地拷贝所有子对象,新的组合对象 ...
- 01.Wireshark入门
Wireshark官网下载地址: https://www.wireshark.org/#download
- 让LED程序在片外SDRAM中运行
让LED程序在片外SDRAM中运行 一.引子 在前一篇文章中,我们已经成功点亮过LED了,为什么还要再重复一次呢? 我们已经知道,Mini2440开发板有两种启动模式:从NorFlash启动和从Nan ...
- 一文入门Kafka,必知必会的概念通通搞定
Kakfa在大数据消息引擎领域,绝对是没有争议的国民老公. 这是kafka系列的第一篇文章.预计共出20篇系列文章,全部原创,从0到1,跟你一起死磕kafka. 本文盘点了 Kafka 的各种术语并且 ...