java time 周期性执行,详解ScheduledExecutorService的周期性执行方法
详解 ScheduledExecutorService 的周期性执行方法
在最近的工作中,需要实现一个当一个任务执行完后,再等 100 毫秒然后再次执行的功能。当时最先反映到的就是 java 线程池的 ScheduledExecutorService,而 ScheduledExecutorService 有两个周期性执行任务的方法,分别是 scheduleAtFixedRate 与 scheduleWithFixedDelay,当时对这两个方法也不大了解,感觉和我的理解有所偏差,所以对这两个方法进行了研究。
ScheduledExecutorService 的基本原理
想要了解 scheduleWithFixedDelay 和 scheduleAtFixedRate 这两个周期性执行任务的方法,首先要了解 ScheduledExecutorService 的原理。在《java 并发编程的艺术》一书中有详细的解说,这里就简单的阐述一下。
ScheduledExecutorService 与其他线程池的区别,主要在于在执行前将任务封装为ScheduledFutureTask与其使用的阻塞队列DelayedWorkQueue。
ScheduledFutureTask
private class ScheduledFutureTask
extends FutureTask implements RunnableScheduledFuture {
/** 表示这个任务添加到ScheduledExecutorService中的序号 */
private final long sequenceNumber;
/** T表示这个任务将要被执行的具体时间(时间戳) */
private long time;
/**
* 表示任务执行的间隔周期,若为0则表示不是周期性执行任务
*/
private final long period;
/*省略以下代码*/
}
DelayedWorkQueue
DelayedWorkQueue 是一个优先队列,在元素出队时,ScheduledFutureTask 的 time 最小的元素将优先出队,如果 time 值相同则判断 sequenceNumber,先入队的元素先出队。
而 DelayedWorkQueue 也是 ScheduledExecutorService 能够定时执行任务的核心类。
首先回顾一下线程池的执行流程:
向线程池提交任务,这时任务将入队到该线程池的阻塞队列
工作线程不断从队列中取出任务,并执行,若然队列中没有任务,工作线程将阻塞直到任务的到来。
当工作线程执行 DelayedWorkQueue 的出队方法时,DelayedWorkQueue 首先获取到 time 值最小的 ScheduledFutureTask,即将要最先执行的任务。然后用 time 值(任务要执行的时间戳)与当前时间作比较,判断任务执行时间是否到期,若然到期,元素立马出队,交由工作线程执行。
但是当 time 值还没到期呢?那么 time 将会减去当前时间,得到 delay 值(延迟多少时间后执行任务),然后使用方法Condition.awaitNanos(long nanosTimeout),阻塞获取任务的工作线程,直到经过了 delay 时间,即到达了任务的执行时间,元素才会出队,交由工作线程执行。
scheduleAtFixedRate 与 scheduleWithFixedDelay
根据我之前的理解,认为 scheduleAtFixedRate 是绝对周期性执行,例如间隔周期为 10 秒,那么任务每隔 10 秒都会执行一次,不管任务是否成功执行。但是我的理解是错误的,这两个方法的功能分别是:
scheduleAtFixedRate:任务执行完成后,在提交任务到任务执行完成后的时间是否经过了 period,若然经过了,即马上再次执行该任务。否则等待,直到提交任务到现在已经经过了 period 时间,再次执行该任务。
scheduleWithFixedDelay:任务执行完成后,等待 delay 时间,然后再次执行。
要清楚,一个定时任务,不管是否为周期性执行,都将会只由一条工作线程执行
首先看下这两个方法的源码
public ScheduledFuture> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask sft =
new ScheduledFutureTask(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture 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 sft =
new ScheduledFutureTask(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
其实两个方法没有太大区别,只是在构建 ScheduledFutureTask 的时候,ScheduledFutureTask 的 period 属性有正负差别,scheduleAtFixedRate 方法构建 ScheduledFutureTask 的 period 为负数,而 scheduleWithFixedDelay 为正数。
接下来查看 ScheduledFutureTask 的 run 方法,工作线程在执行任务时将会调用该方法
/**
* Overrides FutureTask version so as to reset/requeue if periodic.
*/
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);//1
else if (!periodic)
ScheduledFutureTask.super.run();//2
else if (ScheduledFutureTask.super.runAndReset()) {
//3
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
如果定时任务时周期性执行方法,将会进入到 3 的执行逻辑,当然在这之前将会调用 runAndReset 执行任务逻辑。
当任务逻辑执行完成后,将会调用 setNextRunTime。
/**
* Sets the next time to run for a periodic task.
*/
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
如果 period 为正数数,即执行的方法为 scheduleAtFixedRate,在任务的执行时间上添加 period 时间。
而 period 为负数,即执行的方法为 scheduleWithFixedDelay,将 time 改写为当前时间加上 period 时间。
执行完 setNextRunTime 方法后,将执行 reExecutePeriodic 方法,即重新将该 ScheduledFutureTask 对象,重新添加到队列中,等待下一次执行。
要清楚,不论调用哪个周期性执行方法,都是需要等到任务逻辑执行完成后,才能再次添加到队列中,等待下一次执行。
scheduleAtFixedRate 方法,每次都是在 time 的基础上添加 period 时间,如果任务逻辑的执行时间大于 period,那么在定时任务再次出队前,time 必定是小于当前时间,马上出队被工作线程执行。因为 time 每次都是任务开始执行的时间点。
scheduleWithFixedDelay 方法,每次都将 time 设置为当前时间加上 period,那么轮到定时任务再次出队时,必定是经过了 period 时间,才能被工作线程执行。
总结
对于 ScheduledExecutorService 一定要清楚,周期性执行任务,一定是等到上一次执行完成后,才能再次执行,即每个任务只由一条线程执行。那么要实现当达到一定时候后,不论任务是否执行完成,都将再次执行任务的功能,ScheduledExecutorService 的两个周期性执行方法都是不能实现的。其实也就是对于复杂的时间调度控制,ScheduledExecutorService 并不在行。
java time 周期性执行,详解ScheduledExecutorService的周期性执行方法相关推荐
- 详解ScheduledExecutorService的周期性执行方法
2019独角兽企业重金招聘Python工程师标准>>> 详解 ScheduledExecutorService 的周期性执行方法 在最近的工作中,需要实现一个当一个任务执行完后,再等 ...
- java setpaintmode 用法_详解Paint的各种set方法
(1) 在application配置文件中关闭硬件加速: android:allowBackup="true"android:hardwareAccelerated="f ...
- JAVA 多线程并发超详解
JAVA 多线程并发超详解(未完,下一篇文章还有) 1. JAVA 多线程并发 1.1.1. JAVA 并发知识库 1.1.2. JAVA 线程实现/创建方式 1.1.2.1. 继承 Thread 类 ...
- Java线程池ThreadPool详解
Java线程池ThreadPool详解 1. 线程池概述 1.1 线程池简介 1.2 线程池特点 1.3 线程池解决问题 2. 线程池原理分析 2.1 线程池总体设计 2.6 线程池流转状态 2.2 ...
- Java单元测试之JUnit4详解
2019独角兽企业重金招聘Python工程师标准>>> Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @B ...
- java -jar 和 -cp详解
java -jar 和 -cp详解 命令行执行程序 假如我们有一个程序,把它打包成Test.jar,如何运行才能成功输出Hello World package com.test; public cla ...
- 你真的弄明白了吗?Java并发之AQS详解
你真的弄明白了吗?Java并发之AQS详解 带着问题阅读 1.什么是AQS,它有什么作用,核心思想是什么 2.AQS中的独占锁和共享锁原理是什么,AQS提供的锁机制是公平锁还是非公平锁 3.AQS在J ...
- java定时任务框架elasticjob详解
这篇文章主要介绍了java定时任务框架elasticjob详解,Elastic-Job是ddframe中dd-job的作业模块中分离出来的分布式弹性作业框架.该项目基于成熟的开源产品Quartz和Zo ...
- Java中JDBC连接数据库详解
今天动力节点java学院小编分享的是JDBC连接数据库的相关知识,希望通过看过此文,各位小伙伴对DBC连接数据库有所了解,下面就跟随小编一起来看看JDBC连接数据库的知识吧. 一.JDBC连接数据库概 ...
最新文章
- nginx不同server不同日志文件_招标里的答疑是什么?和澄清文件有何不同?
- UITextView实现PlaceHolder的方式
- Mybatis获得参数值的两种方式:#{}和${}的区别
- 转: 如何从keystore file中查看数字证书信息
- java 监听队列_spring+activemq实战之配置监听多队列实现不同队列消息消费
- python3精要(84)-字节码原理及分析(2)
- HUAWEI nova 青春版闪速快充,让追剧不再断电
- 显示控制器注释_欧姆龙可编程控制器CS1D-CPU产品型号说明及功能介绍
- 2013计算机一级考试综合试题答案,2013全国计算机等级考试试题题库及答案.doc
- SimpleXML操控XML
- 双击ctrl搜索 意在颠覆用户的习惯
- 11.云计算平台(数据科学概论)
- vector的初始化和使用
- 供应链金融如何促进产业融合?
- 非平衡电桥电阻计算_非平衡电桥的原理和应用 - 范文中心
- 天津麒麟收购中标软件,国产操作系统新旗舰扬帆起航!
- 凭借这份《2022测试面经》候选者逆袭面试官,offer拿到手软
- 谈谈 DNS 原理及“域名劫持”和“域名欺骗/域名污染”
- 可爱的小老鼠计算机教案,大班语言儿歌教案《小老鼠玩电脑》
- vue使用video和vue-video-player并且可实现视频铺满呦