Java定时任务(一)  Timer及TimerTask的案例解析及源码分析

一、概述:

定时任务这个概念在Java的学习以及项目的开发中并不陌生,应用场景也是多种多样。比如我们会注意到12306网站在锁定车票的30分钟以内要进行车票费用的支付,否则订单会被取消;再比如我们的考试系统,考试管理人员上传好考试信息,考试系统会根据考试信息准时开考;还有上课的铃声会在每周的上课时间准时响起,等等。这些生活细节只要我们稍微注意,留心观察,便会发现编程已经早早地融入到我们生活的方方面面,定时任务也随之应用到这些程序之中。因此,了解好定时任务,对我们的程序的编写有很大的帮助。(更好的阅读体验,请移步我的个人博客)

二、使用案例:

在Java语言中实现定时任务这个功能,我们可以使用Java提供的Timer以及TimerTask来进行简单的实现,jdk1.5之后也可以使用ScheduledExcecutorService 进行实现,后续的文章(定时任务(二))会介绍这种实现方式,并会在定时任务(三)(最后一篇)中介绍一个常见的应用场景中定时任务的实现案例。

这里实现的案例是: 指定一个日期,并间隔一定的时间,周期性的执行一个任务,代码如下:

public class TimerTaskTest {public static void main(String[] args) throws ParseException {//创建定时任务TaskTest taskTest1 = new TaskTest("taskTest1");//定时器创建Timer timer = new Timer("定时器线程");//指定开始执行的日期Date startDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2019-03-03 10:55:10");//时间间隔,单位是mslong intervalTime = 2 * 1000; try {timer.schedule(taskTest1, startDate,intervalTime);} catch (Exception e) {e.printStackTrace();}}
}
class TaskTest extends TimerTask{//  测试任务名称private String taskTestName;public TaskTest(String taskTestName) {super();this.taskTestName = taskTestName;}@Overridepublic void run() {//这里是需要执行的具体任务System.out.printf("定时任务  %s 执行中,响铃响一下\n",taskTestName);}
}

三、实现原理分析:

上面实现的定时任务,主要用到两个类,一个是Timer类,一个是TimerTask类。Timer类主要用于维护定时任务,管理任务的执行,TimerTask是具体执行任务的类。Timer类的schedule方法可以设置任务的运行时机,在上面的案例中,运行方式为:从startDate时间点开始,每隔intervalTime时间重复执行,执行的任务是taskTest1;TaskTest这个类继承自TimerTask,并重写了run方法,我们需要实际运行的任务代码就是放在这里。

Timer是定时器类,当实例化一个Timer对象的时候,这时新建了一个TaskQueue任务队列并且新建了一个线程;任务队列主要存放待执行的任务,线程主要负责管理待执行的任务。TimerThread线程需要以queue为参数进行构造,如下图:

TaskQueue是任务队列,其实现方式是new了一个TimerTask数组,当调用schedule的时候,是向这个数组里面添加元素,然后根据执行时间的先后对数组元素进行排序,从而确定最先开始执行的任务,如下图:

开启的TimerThread线程,会执行run方法里面的mainLoop函数,这个函数会对TaskQueue队列的首元素进行判断,看是否达到执行时间,如果没有,则进行休眠,休眠时间为队首任务的开始执行时间到当前时间的时间差。每当timer对象调用schedule方法时,都会向队列添加元素,并唤醒TaskQueue队列上的线程,这时候TimerThread会被唤醒,继续执行mainLoop方法。如下图所示:

mainLoop函数源代码如下,函数执行的是一个死循环,并且加了queue锁,从而保证是线程安全的。通过queue.getMin()找到任务队列中执行时间最早的元素,然后判断元素的state,period,nextExecutionTime,SCHEDULED等属性,从而确定任务是否可执行。主要是判断这几个属性:1,state 属性,如果为取消(即我们调用了timer的cancel方法取消了某一任务),则会从队列中删除这个元素,然后继续循环;2,period 属性,如果为单次执行,这个值为0,周期执行的话,为我们传入的intervalTime值,如果为0,则会移出队列,并设置任务状态为已执行,然后下面的 task.run()会执行任务,如果这个值不为0,则会修正队列,设置这个任务的再一次执行时间,queue.rescheduleMin这个函数来完成的这个操作;3,taskFired 属性, 如果 executionTime<=currentTime 则设置为true,可以执行,否则线程就会进行休眠,休眠时间为两者之差。

/*** 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-emptywhile (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;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, 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();} catch(InterruptedException e) {}}}

四、总结:

Java中的Timer和TimerTask可以实现基础的定时任务, 他的实现主要用到了线程和队列的组合,通过对timer源码的学习,能更进一步的了解线程和队列方面的知识。在实际的项目中,出于性能和资源的考虑,我们可能不会这么简单粗暴的使用这种方式来实现我们需要的定时功能,原因在于,当我们需要定时执行的任务很多的时候,这种方式会生成很多的线程出来,这是很消耗资源的,同时线程的调度也是很占资源的。因此,后面的博客会介绍其他的实现定时任务的方式,通过线程池的方式管理线程,提高线程利用率,从而提高效率。

欢迎一起学习交流。

Java定时任务(一) Timer及TimerTask的案例解析及源码分析相关推荐

  1. java直接内存为什么快_直接内存与 JVM 源码分析

    直接内存(堆外内存) 直接内存有一种叫法,堆外内存. 直接内存(堆外内存)指的是 Java 应用程序通过直接方式从操作系统中申请的内存.这个差别与之前的堆.栈.方法区,那些内存都是经过了虚拟化.所以严 ...

  2. Java并发基础:了解无锁CAS就从源码分析

    CAS的全称为Compare And Swap,直译就是比较交换.是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令,在i ...

  3. Java并发基础:了解无锁CAS就从源码分析 1

    CAS的全称为Compare And Swap,直译就是比较交换.是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令,在i ...

  4. java usb摄像头_Android中多USB摄像头解决方案——UVCCamera源码分析(一)

    前言 前段时间捣鼓多USB摄像头的方案,一阵手忙脚乱算是勉强跑起来了.整个流程主要还是依赖于网上大神们封装好的库.之前想仔细分析一下整套底层实现,然而一直拖到现在--也没有完全看完,于是想着干脆分阶段 ...

  5. Java的wait()、notify()学习三部曲之一:JVM源码分析

    原文链接:https://blog.csdn.net/boling_cavalry/article/details/77793224 综述 Java的wait().notify()学习三部曲由三篇文章 ...

  6. Java分布式跟踪系统Zipkin(六):Brave源码分析-Brave和SpringBoot整合

    所有博文均在个人独立博客http://blog.mozhu.org首发,欢迎访问! Zipkin是用当下最流行的SpringBoot开发的,SpringBoot将Spring项目的开发过程大大简化,一 ...

  7. java 获取httpsession_java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  8. java解析java源码_JAVA语言-Java源码解析-Stack源码分析

    一.简介 stack类图.png 栈是数据结构中一种很重要的数据结构类型,因为栈的后进先出功能是实际的开发中有很多的应用场景.Java API中提供了栈(Stacck)的实现.Stack类继承了Vec ...

  9. Java 工具(jmap,jstack)在linux上的源码分析(四)safe point

    safe point 顾明思意,就是安全点,当需要jvm做一些操作的时候,需要把当前正在运行的线程进入一个安全点的状态(也可以说停止状态),这样才能做一些安全的操作,比如线程的dump,堆栈的信息. ...

最新文章

  1. dos命令添加war包
  2. 用python函数画德国国旗代码_python海龟绘图之画国旗实例代码
  3. base64编码的学习和理解
  4. Juniper Space License Issue on Citrix Xen Environment
  5. 第三篇 Entity Framework Plus 之 Query Cache
  6. Quasar和Akka –比较
  7. 160 - 6 aLoNg3x.1
  8. 想成为企业争抢的目标吗?你需要掌握的五大热门IT技能
  9. C语言通过网络实现发送文件的一点记录
  10. angular 居中_Angular Material design设计
  11. 算法笔记-----归并排序
  12. [python] 为 pip 更换国内源
  13. [转载]使用Response.Filter过滤非法词汇
  14. css的样式局部作用,局部套用CSS样式.PPT
  15. 【毕业设计】基于STM32的公交站牌系统 - 物联网 嵌入式 单片机
  16. 控制物体沿椭圆轨迹运动
  17. 上百套房子装修样板!
  18. 【西语】【2】Recuërdame antes de que se desaparezca la memoria del amor 在爱的记忆消失前,请记住我
  19. 2021年煤矿安全监测监控新版试题及煤矿安全监测监控
  20. 【小知识】opencv里去掉小连通区域的函数remove_small_objects()解析

热门文章

  1. ASP.NET的HTTP请求处理方法?
  2. 【整数转字符串】LeetCode 9. Palindrome Number
  3. 关于使用DFS,BFS的一些思考总结
  4. 关于被忽略的转置矩阵的公式
  5. CC1101魔幻的收发切换机制
  6. Excel按某一列排序
  7. PyTorch 中如何指定GPU
  8. android 系统dialog的应用
  9. 软工网络15个人作业5--软件工程总结
  10. poj 1322 Chocolate (概率dp)