1. 基本概念

  java.util.Timer:是一个实用工具类,该类用来调度一个线程(schedule a thread),使它可以在将来某一时刻执行。 Java的Timer类可以调度一个任务运行一次,或定期循环运行。 Timer tasks should complete quickly. 即定时器中的操作要尽可能花费短的时间。

  java.util.TimerTask:是一个抽象类,它实现了Runnable接口。我们需要扩展该类以便创建自己的TimerTask,这个TimerTask可以被Timer调度。

  注意:默认, task执行线程不是daemon线程, 任务执行完,主线程(或其他启动定时器的线程)结束时,task线程并没有结束。如果调用者想要快速终止计时器的任务执行线程,调用者应该调用timer.cancel()方法。

public void timerTest() {Timer myTimer = new Timer();myTimer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1s后运行");myTimer.cancel(); // 需要手动cancel}}, 1000);
}

2. 源码解析及案例

  Timer类是线程安全的,多进程不需要外部同步机制就可以共享同一个Timer对象。创建Timer对象,会创建一个java.util.TaskQueue实例,在执行定时任务时,将taskqueue对象作为锁,在指定时间间隔添加任务,在任何时刻只能有一个线程执行TimerTask。
  Timer类使用对象的wait和notify方法来调度任务。

  查看Timer源代码:

// TaskQueue队列,内部就是一个TimerTask[]数组
private final TaskQueue queue = new TaskQueue();
// Timer内部维持一个叫TimerThread的线程,传递TaskQueue队列
private final TimerThread thread = new TimerThread(queue);
// 创建Timer即启动线程
public Timer(String name) {thread.setName(name);thread.start();// 启动线程,后面有分析TimerThread的run方法
}public void schedule(TimerTask task, long delay) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");sched(task, System.currentTimeMillis()+delay, 0);
}
// 核心调度方法,time表示执行的绝对时间
private void sched(TimerTask task, long time, long period) {...// timer对象中的queue作为锁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为调度状态task.state = TimerTask.SCHEDULED;}// 将当前待执行的task添加到队列中queue.add(task);// 队列中取出的head task为当前taskif (queue.getMin() == task)// 在任何时刻只能有一个线程执行TimerTaskqueue.notify();}
}

  其中Timer中启动的TimerThread的run方法:

private TaskQueue queue;
TimerThread(TaskQueue queue) {this.queue = queue;
}
public void run() {try {mainLoop();} finally {// Someone killed this Thread, behave as if Timer cancelledsynchronized(queue) {newTasksMayBeScheduled = false;queue.clear();  // Eliminate obsolete references}}
}private void mainLoop() {while (true) {try {TimerTask task;boolean taskFired;synchronized(queue) {// 如果queue队列为空,则将线程阻塞,等待taskwhile (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 thinglong currentTime, executionTime;// 队列中获取tasktask = queue.getMin();synchronized(task.lock) {// 此处说明可以通过cancel()设置终止task的执行,但TimerThread并没有终止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, removequeue.removeMin();task.state = TimerTask.EXECUTED;} else { // Repeating task, reschedulequeue.rescheduleMin(task.period<0 ? currentTime   - task.period: executionTime + task.period);}}}// 还未到执行时间,则等待相应的时间if (!taskFired) // Task hasn't yet fired; waitqueue.wait(executionTime - currentTime);}if (taskFired)  // Task fired; run it, holding no lockstask.run(); // 执行task的run方法!} catch(InterruptedException e) {}}
}

  TimerTask的调度流程:由以上分析可知,当我们创建一个Timer时,同时内部创建了一个TimerThread线程,并启动它,该线程会不断扫描从Timer传递进来的task队列,如果为空,则wait()阻塞该线程;当timer调用shedule方法的时候,将传递的task添加到队列中,同时调用queue.notify()方法唤醒TimerThread线程,则从队列中取出task根据给定的等待时间wait等待,等待完成后执行task.run();启动任务。(这种结构可以应用到简单的爬虫中)
  

  如何终止Timer线程
  默认情况下,创建的timer线程会一直执行,一般通过下面的方式来终止timer线程:

  1. 调用timer的cancle方法
  2. 把timer线程设置成daemon线程,(new Timer(true)创建daemon线程),在jvm里,如果所有用户线程结束,那么守护线程也会被终止,不过这种方法一般不用。

  下面给出一个Timer执行多个task的简单案例:

public void multileTasksShareOneTimer() {Timer myTimer = new Timer();TimerTask task1 = new TimerTask() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() +" 1s后执行task1");//myTimer.cancel();}};TimerTask task2 = new TimerTask() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() +" 2s后执行task2");//myTimer.cancel();}};myTimer.schedule(task1, 1000);// task2.cancel();myTimer.schedule(task2, 2000);
}

  运行输出:注意此时Timer的TimerThread并没有结束,因为在mainLoop()等待task而wait进入阻塞状态!

  如果设置了task2.cancel();则调度执行task2会抛出异常:

  定时器实际使用的场景还是很多的,比如下面的,在每天零晨备份数据库,等等。

/*** 每天0晨备份数据库*/
@SuppressWarnings("deprecation")
public void backupDatabase() {Timer timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {// 备份数据库System.out.println("数据库备份...");// other operation}};Date firstTime = new Date();firstTime.setHours(0);firstTime.setMinutes(0);firstTime.setSeconds(0);long oneDayPeriod = 24 * 60 * 60 * 1000;timer.scheduleAtFixedRate(task, firstTime, oneDayPeriod);// timer.schedule(task, firstTime, oneDayPeriod);
}

  最后:如果是简单的定时调度,使用Timer就够了,如果复杂的调度任务,可以考虑使用Quartz

  转载请注明出处:Java多线程总结(3)— Timer 和 TimerTask深入分析

Java多线程总结(3)— Timer 和 TimerTask深入分析相关推荐

  1. java的timertask_Java中Timer和TimerTask来实现计时器循环触发

    package xian; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.Fi ...

  2. Java多线程(三)之ConcurrentHashMap深入分析

    一.Map体系 Hashtable是JDK 5之前Map唯一线程安全的内置实现(Collections.synchronizedMap不算).Hashtable继承的是Dictionary(Hasht ...

  3. Java多线程(四)之ConcurrentSkipListMap深入分析

    一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下,ConcurrentHashMap 存取速度是ConcurrentSki ...

  4. Java多线程(五)之BlockingQueue深入分析

    一.概述: BlockingQueue作为线程容器,可以为线程同步提供有力的保障. 二.BlockingQueue定义的常用方法 1.BlockingQueue定义的常用方法如下:   抛出异常 特殊 ...

  5. Java多线程(十)之ReentrantReadWriteLock深入分析

    一.ReentrantReadWriteLock与ReentrantLock 说到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限.它和后者都是单独的实现 ...

  6. Java多线程(一)之volatile深入分析

    volatile 变量提供了线程的可见性,并不能保证线程安全性和原子性. 什么是线程的可见性: 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility).互斥即 ...

  7. java中timer和timertask_使用Java中的Timer和TimerTask

    有的时候我们需要每隔一段时间去执行某个任务,在Java中提供了Timer and TimerTask来完成这个任务,本文提供一个应用程序的源代码告诉你如何使用这两个类. Timer和TimerTask ...

  8. java线程中的task_Java线程(四):Timer和TimerTask

    Timer和TimerTask可以做为实现线程的第三种方式,前两中方式分别是继承自Thread类和实现Runnable接口. Timer是一种线程设施,用于安排以后在后台线程中执行的任务.可安排任务执 ...

  9. Java Timer、TimerTask

    下面内容转载自: http://blog.csdn.net/xieyuooo/article/details/8607220 其实就Timer来讲就是一个调度器,而TimerTask呢只是一个实现了r ...

最新文章

  1. 组建元宇宙军团!「谷歌实验室」重生,超700人神秘团队都有谁?
  2. [MAC OS] 常用工具
  3. 《Python Cookbook 3rd》笔记(4.3):使用生成器创建新的迭代模式
  4. 面向对象第八天---预处理与mysql事务
  5. 华为机试HJ20:密码验证合格程序
  6. [学习笔记] JQuery datepicker用法 [转]
  7. [Javascript]把html内容复制到剪贴板
  8. 像素值、物理尺寸、分辨率三者关系
  9. 人人视频android资源比ios多,人人视频
  10. 交警对开车人的“真言”
  11. “十步杀一人,千里不留行。事了拂衣去,深藏功与名。”
  12. JAVASE基础(十)
  13. staruml 时序图操作
  14. 20200607:根据中证800指数最近十年历史P/b分位数确认基金目标仓位
  15. JS原型和原型链是什么?
  16. NB IoT LWM2M Object or/and Resource Identifier: (Object ID) or (ResourceID)
  17. 《PostgreSQL 开发指南》第 08 篇 备份与恢复
  18. 2022-2028年中国PVC地板行业市场竞争态势及投资方向分析报告
  19. [CSS]分享几条漂亮的下划线样式
  20. 最新电商团队的 OKR案例

热门文章

  1. 手撸SSO单点登录(四)登录验证-首次登录
  2. linux防火墙配置开放端口
  3. JJwt生成Token
  4. android下提取PPS,SPS
  5. 今日份安利:Word转PDF用什么软件?
  6. java包装类型 “==”的比较(如Integer,Double)
  7. 【原创】研究《绿色军团》NSF的音乐引擎(3)再取样再开始
  8. 您不是文件所有者_如何解决您的连接不是私人错误(网站所有者指南)
  9. android studio173,Android Studio 3.1.2 版本包下载
  10. 湖北省211大学计算机分数线,湖北省211大学名单排名榜及分数线 湖北省211大学名单...