欢迎探讨,如有错误敬请指正

如需转载,请注明出处 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的工作原理与使用示例的更多相关文章

  1. Optaplanner规划引擎的工作原理及简单示例(2)

    开篇 在前面一篇关于规划引擎Optapalnner的文章里(Optaplanner规划引擎的工作原理及简单示例(1)),老农介绍了应用Optaplanner过程中需要掌握的一些基本概念,这些概念有且于 ...

  2. JAVA NIO工作原理及代码示例

    简介:本文主要介绍了JAVA NIO中的Buffer, Channel, Selector的工作原理以及使用它们的若干注意事项,最后是利用它们实现服务器和客户端通信的代码实例. 欢迎探讨,如有错误敬请 ...

  3. Optaplanner规划引擎的工作原理及简单示例(1)

    在之前的文章中,老猿已介绍过APS及规划的相关内容,也对Optaplanner相关的概念和一些使用示例进行过介绍,接下来的文章中,我会自己做一个规划小程序 - 一个关于把任务分配到不同的机台上进行作来 ...

  4. Java并发包中CountDownLatch的工作原理、使用示例

    1. CountDownLatch的介绍 CountDownLatch是一个同步工具,它主要用线程执行之间的协作.CountDownLatch 的作用和 Thread.join() 方法类似,让一些线 ...

  5. Java并发包中CyclicBarrier的工作原理、使用示例

    1. CyclicBarrier的介绍与源码分析 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时 ...

  6. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

  7. 浏览器内部工作原理--作者:Tali Garsiel

    本篇内容为转载,主要用于个人学习使用,作者:Tali Garsiel 一.介绍 浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入google.com到你看到 ...

  8. Ajax工作原理

    在写这篇文章之前,曾经写过一篇关于AJAX技术的随笔,不过涉及到的方面很窄,对AJAX技术的背景.原理.优缺点等各个方面都很少涉及null.这次写这篇文章的背景是因为公司需要对内部程序员做一个培训.项 ...

  9. 一篇笔记整理JVM工作原理

    首先要了解的 >>数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型. 基本类型的变量保存原始值,即:他代表的值就是数值本身:而引用类型的变量保存引用值.“引用值”代表了 ...

随机推荐

  1. memset 究竟在做什么?

    我要 一字节一字节 地 memory set

  2. Cobbler学习之二--Cobbler的Web管理和维护

    Cobbler的Web管理模块和命令行模块是可以分开工作的,没有依赖关系. 1 WebUI的功能 查看所有的对象和配置文件 添加或者删除system,distro, profile 执行“cobble ...

  3. 异或链表(XOR linked list)

    异或链表(Xor Linked List)也是一种链式存储结构,它可以降低空间复杂度达到和双向链表一样目的,任何一个节点可以方便的访问它的前驱节点和后继结点.可以参阅wiki 普通的双向链表 clas ...

  4. Linq学习工具及Lamada表达式

    好东西.转载一个.以备学习 Linq学习工具:     http://www.linqpad.net/ Lamada表达式: Func<int, int, int> IntPow = (x ...

  5. jquery 监听input输入值事件

    <html> <head> <title></title> <script type="text/javascript" sr ...

  6. jQUery 1.9中被删除的API

    jQuery1.9删除了一些在以前版本中已经过时的api,想要把那些不够安全的.缺乏效率的.用处不大的,以及带有误导的特性统统去掉.如果你想升级你的jquery版本,但又使用了如下被删除的api的话, ...

  7. 链表反转 (Multi-method)

    链表反转是链表相关问题最基础的知识,做完LeetCode中LinkedList后才会有这种体会,因为ACM算法中不会涉及这一部分.解决这一问题有多种方法,在面试中面试官通常也会要求写出多种.包括sta ...

  8. 带回调函数的js运动框架

    function startMove(obj, json, endFun) { //开始前关闭之前obj上的定时器 clearInterval(obj.timer); //定时器 obj.timer ...

  9. delphi 读写文本

    将字符串写入txt文档,读取txt文档中的内容. //一次写字符串到文本文件,每次都会将原来的内容替换掉. procedure FilePutContents(f,s:String); // f为文件 ...

  10. 查询Oracle锁表和解决方法

    Oracle数据库操作中,我们有时会用到锁表查询以及解锁和kill进程等操作,那么这些操作是怎么实现的呢?本文我们主要就介绍一下这部分内容.(1)锁表查询的代码有以下的形式:select count( ...