ScheduleThreadPoolExecutor的工作原理与使用示例
如需转载,请注明出处 http://www.cnblogs.com/nullzx/
1. ScheduleExecutorService接口、ScheduledFuture接口

从图中可以看出ScheduledExecutorService接口继承了ExecutorService接口,同时还添加了有关提交定时任务的四个方法。
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
//向定时任务线程池提交一个延时Runnable任务(仅执行一次)
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
//向定时任务线程池提交一个延时的Callable任务(仅执行一次)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
                                                  long period, TimeUnit unit)
//向定时任务线程池提交一个固定时间间隔执行的任务
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
                                                      long delay, TimeUnit unit);
//向定时任务线程池提交一个固定延时间隔执行的任务
固定时间间隔的任务不论每次任务花费多少时间,下次任务开始执行时间是确定的,当然执行任务的时间不能超过执行周期。
固定延时间隔的任务是指每次执行完任务以后都延时一个固定的时间。由于操作系统调度以及每次任务执行的语句可能不同,所以每次任务执行所花费的时间是不确定的,也就导致了每次任务的执行周期存在一定的波动。
注意:定时或延时任务中所涉及到时间、周期不能保证实时性及准确性,实际运行中会有一定的误差。
从上图可以还可以看出ScheduledThreadPoolExecutor还直接继承了ThreadPoolExecutor。这样做是为了利用ThreadPoolExecutor已实现的方法。
可以向定时任务线程池提交普通任务。对于定时任务线程池而言,普通任务只不过是延时执行时间为0,周期为0的任务。
从上述四个方法中的返回值可以看出,当向线程池提交任务时会返回一个ScheduleFuture接口的对象。通过下图可以看出,ScheduledFuture接口继承了Delayed和Future接口。我们可以通过ScheduleFutured对象的cancel方法可以结束一个定时任务。

在ScheduledThreadPoolExecutor中阻塞队列存储的是ScheduledFutureTask对象,它是任务真正的载体。但是ScheduledFutureTask对象同样可以看做Runnable对象,所以同样可以使用ThreadPoolExecutor中的方法来处理它。
2. ScheduledThreadPoolExecutor的运行原理

在图中,我们将ScheduleFutureTask简称为SFT
在定时任务线程池中存储任务的队列是具有优先性质的阻塞队列。在这个队列中离下次执行时间最近的任务位于队首(关于优先队列的原理请参照本博客后续数据结构的相关内容)。线程每次通过优先队列的take方法获取任务。如果队列为空take方法阻塞,否则take方法从队首获取带执行的任务(即ScheduleFutureTask对象),然后从任务的getDelay方法获取应当延时的时间,再通过带参数的await方法进行延时。当延时时间已到,则开始执行任务(在执行之前还要检测这个任务是否已被取消),执行完毕以后继续判断这是否是一个周期任务。如果不是,调用take方法获取新任务,重复上述操作。如果是计算下一次执行的时间,然后调用队列的add方法将这个任务再次入列。入列完成后,继续调用take方法获取新任务,这样重复的执行下去。
以上就是定时任务线程池工作的最基本原理,实际上take、add、任务的取消等过程比较复杂,图中也没有说明线程在执行await方法时队首元素改变时的情况。如果想继续了解,请参阅本博客“ScheduledThreadPoolExecutor源代码分析”的文章。
3. ScheduleThreadPoolExecutor与Timer相比的优势。
(1)Timer是基于绝对时间的延时执行或周期执行,当系统时间改变,则任务的执行会受到的影响。而ScheduleThreadPoolExecutore中,任务时基于相对时间进行周期或延时操作。
(2)Timer也可以提交多个TimeTask任务,但只有一个线程来执行所有的TimeTask,这样并发性受到影响。而ScheduleThreadPoolExecutore可以设定池中线程的数量。
(3)Timer不会捕获TimerTask的异常,只是简单地停止,这样势必会影响其他TimeTask的执行。而ScheduleThreadPoolExecutore中,如果一个线程因某些原因停止,线程池可以自动创建新的线程来维护池中线程的数量。
4. 使用示例
package javalearning;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExecutorDemo {
	static class TimerTask implements Runnable{
		private String id;
		public TimerTask(String id){
			this.id = id;
		}
		@Override
		public void run(){
			System.out.println(id);
		}
	}
	public static void main(String[] args) throws InterruptedException{
		ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);
		ScheduledFuture sfa = ses.scheduleAtFixedRate(new TimerTask("a"), 200,
				                                    1000, TimeUnit.MILLISECONDS);
		ScheduledFuture sfb = ses.scheduleAtFixedRate(new TimerTask("b"), 400,
				                                    1000, TimeUnit.MILLISECONDS);
		ScheduledFuture sfc = ses.scheduleAtFixedRate(new TimerTask("c"), 600,
				                                    1000, TimeUnit.MILLISECONDS);
		ScheduledFuture sfd = ses.scheduleAtFixedRate(new TimerTask("d"), 800,
				                                    1000, TimeUnit.MILLISECONDS);
		Thread.sleep(5000);
		sfa.cancel(true);
		Thread.sleep(5000);
		ses.shutdown();
	}
}
在这个示例中,定义了一个内部类TimerTask,它实现了Runnable接口。在main方法中创建了一个定时任务线程池,向它提交了四个任务a,b,c,d。四个任务起始的延时时间分别是200ms、400ms、600ms、800ms,执行周期都为1000ms。5秒以后取消了定时任务a的执行。又过了5秒,关闭了线程池。默认情况下关闭线程池会结束池所有的任务。
以下是运行结果
a
b
c
d
a
b
c
d
a
b
c
d
a
b
c
d
a
b
c
d
b
c
d
b
c
d
b
c
d
b
c
d
b
c
d
5.参考内容
[1] http://janeky.iteye.com/blog/770441
ScheduleThreadPoolExecutor的工作原理与使用示例的更多相关文章
- Optaplanner规划引擎的工作原理及简单示例(2)
		开篇 在前面一篇关于规划引擎Optapalnner的文章里(Optaplanner规划引擎的工作原理及简单示例(1)),老农介绍了应用Optaplanner过程中需要掌握的一些基本概念,这些概念有且于 ... 
- JAVA NIO工作原理及代码示例
		简介:本文主要介绍了JAVA NIO中的Buffer, Channel, Selector的工作原理以及使用它们的若干注意事项,最后是利用它们实现服务器和客户端通信的代码实例. 欢迎探讨,如有错误敬请 ... 
- Optaplanner规划引擎的工作原理及简单示例(1)
		在之前的文章中,老猿已介绍过APS及规划的相关内容,也对Optaplanner相关的概念和一些使用示例进行过介绍,接下来的文章中,我会自己做一个规划小程序 - 一个关于把任务分配到不同的机台上进行作来 ... 
- Java并发包中CountDownLatch的工作原理、使用示例
		1. CountDownLatch的介绍 CountDownLatch是一个同步工具,它主要用线程执行之间的协作.CountDownLatch 的作用和 Thread.join() 方法类似,让一些线 ... 
- Java并发包中CyclicBarrier的工作原理、使用示例
		1. CyclicBarrier的介绍与源码分析 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时 ... 
- 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL
		周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ... 
- 浏览器内部工作原理--作者:Tali Garsiel
		本篇内容为转载,主要用于个人学习使用,作者:Tali Garsiel 一.介绍 浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入google.com到你看到 ... 
- Ajax工作原理
		在写这篇文章之前,曾经写过一篇关于AJAX技术的随笔,不过涉及到的方面很窄,对AJAX技术的背景.原理.优缺点等各个方面都很少涉及null.这次写这篇文章的背景是因为公司需要对内部程序员做一个培训.项 ... 
- 一篇笔记整理JVM工作原理
		首先要了解的 >>数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型. 基本类型的变量保存原始值,即:他代表的值就是数值本身:而引用类型的变量保存引用值.“引用值”代表了 ... 
随机推荐
- P1906联合权值
			描述 无向连通图 G 有 n 个点,n-1 条边.点从 1 到 n 依次编号,编号为 i 的点的权值为 WiWi, 每条边的长度均为 1.图上两点(u, v)的距离定义为 u 点到 v 点的最短距离. ... 
- mysql 存储 date , datetime问题,初步
			1. java 里的 Date date = new Date()(java.util.Date) 得到 Thu Nov 03 22:19:43 CST 2016, 通过Timestamp stam ... 
- 编译器错误信息: CS0433: 类型“ASP.usercontrols_total_ascx”同时存在
			“/”应用程序中的服务器错误. 编译错误 说明: 在编译向该请求提供服务所需资源的过程中出现错误.请检查下列特定错误详细信息并适当地修改源代码. 编译器错误信息: CS0433: 类型“ASP.use ... 
- 结对开发训练(郭林林&胡潇丹)
			此次编程题为:求一个整数数组最大子数组之和,要求时间复杂度为O(n). 首先,我们对题目做出分析,做出第一种预行方案,即定义一个数组,当数组中元素大于等于0时,进行累加:若小于0,则与后面的数作比较, ... 
- Cocoapods的安装,卸载和使用
			[一]Cocoapods的安装 (1)Cocoapods的官方网站为: https://cocoapods.org/ .如果你的电脑已经安装了Ruby开发环境,那么在终端(Terminal)中使用以下 ... 
- 使用UG UISTYLER 窗体编辑器,创建对话框 part 2
			接下来看看自动生成的cs文件里的东西吧,下面是之前保存的窗体的cs文件: 如果仅仅做一些UG开发没有太多的语言基础,那并不需要去了解初始函数内的东西.只需要了解下3类入口函数如何修改和如何应用就可以了 ... 
- 【转】java.lang.OutOfMemoryError: Java heap space的解决
			原文地址:http://blog.sina.com.cn/s/blog_4b12778b0100v0bb.html Myeclipse下java.lang.OutOfMemoryError: Java ... 
- Manifesto of the Communist Party
			A spectre is haunting Europe – the spectre of communism. All the powers of old Europe have entered i ... 
- android权限permission大全
			1.Android.permission.WRITE_USER_DICTIONARY允许应用程序向用户词典中写入新词 2.android.permission.WRITE_SYNC_SETTINGS写 ... 
- 算法:POJ1006 三重峰值问题
			这题有直接套公式的解法 这里提供一个O(n)的解法. package practice; import java.io.BufferedInputStream; import java.util.Sc ... 
