Java原生api Timer类就可以实现简单的定时任务。下面将简单介绍一下Timer。

一、使用 Timer 实现定时任务

具体代码如下。

可以看到我们主要是分三步进行的

1、new Timer() 创建定时器

2、new TimerTask() 创建任务。这里是通过继承TimerTask类实现的。

3、使用定时器调度。这里我们使用的是Timer类中的schedule(TimerTask task, long delay, long period) 方法,里面有三个参数,

   第一个参数表示任务,第二个参数表示延迟的时间,第三个参数表示任务执行的间隔(也就是每隔多少秒执行一次任务)。

public class TimerTest {

    public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTest().new MyTask(), 1000, 5000);
} /**
* 任务
* @author LKB
*
*/
class MyTask extends TimerTask{ @Override
public void run() {
// TODO Auto-generated method stub
System.out.println("current time is " + new Date());
}
}
}

二、Timer 实现定时任务的原理

Timer 的实现需要四个类:Timer TimerThread TaskQueue TimerTask。

2.1 TimerTask

其中 TimerTask 表示任务,它是个抽象类TimeTask 抽象类  继承Runnable。可以看到每个Task都是一个runnable对象。

TimeTask中全局变量有

   lock    锁
state VIRGIN: 任务未被调度
SCHEDULED: 任务被调度但是还未执行
EXECUTED: 任务已经被执行但是未被取消
CANCELLED:任务被取消
nextExecutionTime:任务下一次被执行的时间
period:重复任务之间的间隔

内部方法有

  //取消调度
public boolean cancel() {
synchronized(lock) {
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
//计算下次执行时间
public long scheduledExecutionTime() {
synchronized(lock) {
return (period < 0 ? nextExecutionTime + period
: nextExecutionTime - period);
}
}

2.2 TaskQueue

TaskQueue 是一个优先队列,它是通过一个数组实现的。

全局变量有:

  private TimerTask[] queue = new TimerTask[128];  一个数组,数组元素类型为TimerTask。这个数组用来实现优先队列。

    private int size = 0; //优先队列中的任务个数  需要注意的是任务在优先队列中的存储是queue[1]-queue[size]

内部的方法有挺多的,最基本的我认为是下面几个:

  /**
* Adds a new task to the priority queue.
*/
void add(TimerTask task) {
// Grow backing store if necessary
if (size + 1 == queue.length)
queue = Arrays.copyOf(queue, 2*queue.length); queue[++size] = task;
fixUp(size);
} /**
* Return the "head task" of the priority queue. (The head task is an
* task with the lowest nextExecutionTime.)
*/
TimerTask getMin() {
return queue[1];
} /**
* Remove the head task from the priority queue.
*/
void removeMin() {
queue[1] = queue[size];
queue[size--] = null; // Drop extra reference to prevent memory leak
fixDown(1);
}

add 是往队列中添加任务,removeMin 是移除队头元素。因为 TaskQueue 是通过优先队列实现的,所以的删除或者添加一个元素的复杂度为O(lgN)。

2.3 TimerThread

TimerThread 继承Thread 类,Timer 定时器正是通过这个线程实现任务调度的。

全局变量有

boolean newTasksMayBeScheduled = true;  //如果该标志位false 则表示对 Timer 对象没有任何有效的引用,没有任何的任务需要做了,线程可以优雅地终止了
private TaskQueue queue; //任务队列

重写的 run  方法如下

    public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
} /**
* The main timer loop. (See class comment.)
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die // Queue nonempty; look at first evt and do the right thing
long currentTime, executionTime;
task = queue.getMin();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // No action required, poll queue again
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired) // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}

可以看到TimerThread 运行的方式很简单,在一个while(true)循环中,首先判断任务队列中是否有任务,没有任务则一直等待;

如果有任务,取出该任务(运行时间离当前时间最近的任务),判断任务时间,任务时间到了,则调用task.run()运行任务。

2.4 Timer

Timer 是一个调度器,该调度器内部只有一个调度线程,并且只有一个任务队列。

全局变量有:

   private final TaskQueue queue = new TaskQueue(); //任务队列
  private final TimerThread thread = new TimerThread(queue); //调度线程
  private final Object threadReaper = new Object() {
protected void finalize() throws Throwable {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.notify(); // In case queue is empty.
}
}
}; //该对象可以使得当没有任何有效引用指向Timer对象时,定时器被优雅回收 /**
* This ID is used to generate thread names.
*/
private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
private static int serialNumber() {
return nextSerialNumber.getAndIncrement();
}

最基本的构造器如下。可以看到,当我们new一个 Timer 对象出来时,Timer 中对应的调度线程就被启动了。

 public Timer(String name) {
thread.setName(name);
thread.start();
}

最基本的调度方法如下。可以看出它其实就是对任务队列的一个操作。

首先将当前任务配置好(计算好下次执行时间,间隔与状态),然后加入任务队列,如果刚好是队列的队头,再将队列唤醒,让队列直接处理。

 private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time."); // Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1; synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled."); synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
} queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}

综上,我们可以任务定时器的工作原理如下:

Timer 内部拥有一个 TimerThread 和 一个TaskQueue,当我们new Timer() 时,我们就拥有了这两个对象,并启动了TimerThread。

当我们这样调动  timer.schedule(new TimerTest().new MyTask(), 1000, 5000); schedule方法时我们就将对应的任务加到TaskQueue中。

TimeThread 负责调度TaskQueue,TaskQueue 是一个优先队列,里面的元素全是TimerTask 对象。

【定时任务】Timer的更多相关文章

  1. Java基础--定时任务Timer

    Java基础--定时任务Timer 一.Timer介绍 java.util.Timer java.util.TimerTask Timer是一个定时器类,通过该类可以为指定的定时任务进行配置.Time ...

  2. Java基础--定时任务Timer(转载)

    Java基础--定时任务Timer 一.Timer介绍 java.util.Timer java.util.TimerTask Timer是一个定时器类,通过该类可以为指定的定时任务进行配置.Time ...

  3. JAVA定时任务Timer

    故事起因 因业务需要,写了一个定时任务Timer,任务将在每天的凌晨2点执行,代码顺利码完,一切就绪,开始测试.运行程序,为了节省时间,将系统时间调整为第二天凌晨1点59分,看着秒针滴答滴答的转动,期 ...

  4. Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解

     定时任务就是在指定时间执行程序,或周期性执行计划任务.Java中实现定时任务的方法有很多,本文从从JDK自带的一些方法来实现定时任务的需求. 一.Timer和TimerTask  Timer和Tim ...

  5. 详解java定时任务---Timer篇

    一.简介      在java的jdk中提供了Timer.TimerTask两个类来做定时任务. Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,而TimerTask一个抽象类,它的子 ...

  6. Java之旅--定时任务(Timer、Quartz、Spring、LinuxCron)

    在Java中,实现定时任务有多种方式,本文介绍4种,Timer和TimerTask.Spring.QuartZ.Linux Cron. 以上4种实现定时任务的方式,Timer是最简单的,不需要任何框架 ...

  7. 服务器启动完成执行定时任务Timer,TimerTask

    由于项目需求:每隔一段时间就要调外部接口去进行某些操作,于是在网上找了一些资料,用了半天时间弄好了,代码: import java.util.TimerTask; public class Accou ...

  8. java定时任务Timer与ScheduledExecutorService<转>

    在我们编程过程中如果需要执行一些简单的定时任务,无须做复杂的控制,我们可以考虑使用JDK中的Timer定时任务来实现.下面LZ就其原理.实例以及Timer缺陷三个方面来解析java Timer定时器. ...

  9. java web定时任务---Timer

    写在前面: 在最近的项目中需要每天定时对数据库表进行查询,并完成相关数据的更新操作.首先让我想到的是Timer类,记得在一开始维护那个老系统的时候,开了个接口,也涉及到了定时的操作.下面就记录下大概的 ...

  10. 定时任务-Timer

    Timer类的全限定名 java.util.Timer java.util.Timer类的构造函数 public Timer(); public Timer(boolean isDaemon); pu ...

随机推荐

  1. 【AtCoder Regular Contest 080E】Young Maids [堆][线段树]

    Young Maids Time Limit: 50 Sec  Memory Limit: 512 MB Description 给定一个排列,每次选出相邻的两个放在队头,要求字典序最小. Input ...

  2. hdu 1495 非常可乐 (广搜)

    题目链接 Problem Description 大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为.因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶 ...

  3. summernote 文本编辑器使用时,选择上传图片、链接、录像时,弹出的对话框被遮挡住

    更多内容推荐微信公众号,欢迎关注: 即问题如下链接内的情况: http://bbs.csdn.net/topics/392004332 这个一般属于CSS中样式出现了问题,可以在点开的时候,F12查看 ...

  4. es6解构、中括号前加分号

    在写项目的时候,为了方便使用了下对象的解构,无奈又遇到一坑. 为什么会不能解构呢?因为这里的{}会导致歧义,因为 JavaScript 引擎会将{xxxxx}理解成一个代码块,从而发生语法错误.只有不 ...

  5. 子查询优化--explain与profiling分析语句

    今天想的利用explain与progiling分析下语句然后进行优化.本文重点是如何通过explain与profiling分析SQL执行过程与性能.进而明白索引的重要性. 表的关系如下所示: 原始的查 ...

  6. linux设备驱动之USB主机控制器驱动分析 【转】

    转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html   ---------------------------------------- ...

  7. STM32 IAP升级

    STM32 IAP在线升级,用Jlink设置读保护后前5K字节是默认加了写保护的,导致IAP升级时擦除和写入FLASH不成功,可以做两个boot,前5k为第一个boot程序,上电时负责跳转到APP还是 ...

  8. js对金额浮点数运算精度的处理方案

    浮点数产生的原因 浮点数转二进制,会出现无限循环数,计算机又对无限循环小数进行舍入处理 js弱语言的解决方案 方法一: 指定要保留的小数位数(0.1+0.2).toFixed(1) = 0.3;这个方 ...

  9. 项目中遇到的问题:Gradle传递性依赖冲突

    问题描述: 在调用别人接口时,由于他们接口做了拦截处理在使用RestTemplate调用时必须要使用@Qualifier("他们封装好的类"),需要导入jar包 gradle方式导 ...

  10. poj1067

    题意:有两堆石子,两人轮流取,每次可以取一堆中的任意个,或两堆中取相同多个.谁先取光所有堆谁赢.问先手能否获胜. 分析:威佐夫博弈,如果是奇异态则先手输,否则先手赢.直接套用公式判断是否为奇异态,设第 ...