前面介绍了线程的几种运行方式,不管哪种方式,一旦调用了线程实例的start方法,都会立即启动线程的事务处理。然而某些业务场景在事务执行时间方面有特殊需求,例如期望延迟若干时间之后才开始事务运行,又如期望每隔若干时间依次启动事务处理,如此种种都要求在指定的时间才能启动线程任务,也就是俗称的定时功能。
有别于一般的线程,Java为定时功能设计了专门的定时任务TimerTask,以及定时器Timer。其中TimerTask用来描述时刻到达后的事务处理,而Timer用来调度定时任务,包括何时启动定时任务、需要间隔多久才再次运行定时任务等等。
定时任务TimerTask的代码定义类似Runnable,二者均需重写run方法填写任务代码,不同的是,Runnable任务需要实现Runnable接口,定时任务则由TimerTask类派生而来。下面是个计数用的定时任务代码例子:

	// 定义一个用于计数的定时任务
private static class CountTask extends TimerTask {
private int count = 0; // 计数值 @Override
public void run() {
// 以下打印计数日志,包括当前时间、当前线程、计数值等信息
PrintUtils.print(Thread.currentThread().getName(), "当前计数值为"+count);
count++;
}
}

接下来轮到让定时器来调度定时任务,定时器Timer的调度方法主要有schedule和scheduleAtFixedRate两个,不过schedule重载了多个同名方法,依据重载参数的数量区别,可将调度方法划分为下列三类用途:

1、带两个参数的schedule方法,其中第一个参数为定时任务,第二个参数为任务的启动时间或者延迟启动间隔。这种schedule方法只会启动惟一一次定时任务。
2、带三个参数的schedule方法,其中第一个参数为定时任务,第二个参数为任务的启动时间或者延迟启动间隔,第三个参数为之后继续启动的时间间隔。这种schedule方法会持续不断地启动定时任务。
3、scheduleAtFixedRate方法,其中第一个参数为定时任务,第二个参数为任务的启动时间或者延迟启动间隔,第三个参数为之后每次启动的时间间隔。scheduleAtFixedRate方法也会持续不断地启动定时任务。
后面两种调度方式,乍看之下没什么区别,都是每隔一段时间启动后续的任务。其实还是有点小区别的,带三个参数的schedule方法,下个任务要在上个任务结束之后再间隔若干时间才启动;至于scheduleAtFixedRate方法,下个任务不管上个任务何时结束,只要相互之间的启动间隔到达,即可立即启动下个任务。所以呢,schedule方式的下次启动时间与任务执行耗时有关,而scheduleAtFixedRate方式与任务耗时无关,它才是真正意义上以固定频率运行着的定时调度。
讲完了定时器的几种调度方式,再来看定时器的具体操作代码,以schedule方法为例,通过该方法延迟若干时间后启动定时任务的代码如下所示:

	// 测试只跑一次的定时器调度
private static void testScheduleOnce() {
CountTask timerTask = new CountTask(); // 创建一个计数的定时任务
Timer timer = new Timer(); // 创建一个定时器
// 命令定时器启动定时任务。调度规则为:延迟50毫秒后启动。
timer.schedule(timerTask, 50);
try {
Thread.sleep(1000); // 睡眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
timer.cancel(); // 取消定时器
}

把上面的schedule方法改为固定间隔启动定时任务的话,只需添加第三个参数就好了,调用代码片段示例如下:

		// 命令定时器启动定时任务。调度规则为:延迟50毫秒后启动,且上一个任务执行完毕间隔100毫秒再执行下一个任务
timer.schedule(timerTask, 50, 100);

或者改成使用scheduleAtFixedRate方法以固定速度启动定时任务,此时的调用代码片段见下:

		// 命令定时器启动定时任务。调度规则为:延迟50毫秒后启动,且之后每间隔100毫秒再执行一个任务
timer.scheduleAtFixedRate(timerTask, 50, 100);

运行以上的定时器代码,观察到以下的定时日志,可见定时任务被放到名叫Timer-0的分线程中执行了:

19:01:49.634 Timer-0 当前计数值为0
19:01:49.661 Timer-0 当前计数值为1
19:01:49.761 Timer-0 当前计数值为2
19:01:49.861 Timer-0 当前计数值为3
………………………这里省略余下的日志……………………

另外注意一点,定时任务TimerTask和定时器Timer都提供了cancel方法,TimerTask的cancel方法取消的是原来的定时任务,取消之后,还能通过定时器来调度新创建的定时任务。而Timer的cancel方法取消的是定时器自身,一旦取消定时器,那么不但原来的定时任务被一块撤销,而且该定时器不能再调度任何一个定时任务,相当于这个定时器彻底报废了,除非再次创建全新的定时器才能开展调度工作。

更多Java技术文章参见《Java开发笔记(序)章节目录

Java开发笔记(九十九)定时器与定时任务的更多相关文章

  1. Java开发笔记(九十一)IO流处理简单的数据压缩

    前面介绍的文件I/O,不管是写入文本还是写入对象,文件中的数据基本是原来的模样,用记事本之类的文本编辑软件都能浏览个大概.这么存储数据,要说方便确实方便,只是不够经济划算,原因有二:其一,写入的数据可 ...

  2. Java开发笔记(九十七)利用Runnable启动线程

    前面介绍了线程的基本用法,按理说足够一般的场合使用了,只是每次开辟新线程,都得单独定义专门的线程类,着实开销不小.注意到新线程内部真正需要开发者重写的仅有run方法,其实就是一段代码块,分线程启动之后 ...

  3. Java开发笔记(九)赋值运算符及其演化

    前面的加减乘除四则运算,计算结果通过等号输出给指定变量,注意此时代码把变量放到等号左边.而在算术课本里,加法运算的完整写法类似于“1+1=2”这样,运算结果应该跟在等号右边.不过代数课本里的方程式存在 ...

  4. Java开发笔记(序)章节目录

    现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...

  5. Java开发笔记(十三)利用关系运算符比较大小

    前面在<Java开发笔记(九)赋值运算符及其演化>中提到,Java编程中的等号“=”表示赋值操作,并非数学上的等式涵义.Java通过等式符号“==”表示左右两边相等,对应数学的等号“=”: ...

  6. Java开发笔记(一百零五)几种定时器线程池

    前面介绍了普通线程池的用法,就大多数任务而言,它们对具体的执行时机并无特殊要求,最多是希望早点跑完早点出结果.不过对于需要定时执行的任务来说,它们要求在特定的时间点运行,并且往往不止运行一次,还要周期 ...

  7. Java开发笔记(九十)对象序列化及其读写

    有些时候,开发者想把程序运行过程中的数据临时保存到文件,可是前面介绍的字符流和字节流,要么用来读写文本字符串,要么用来读写字节数组,并不能直接保存某个对象信息,因为对象里面包括成员属性和成员方法,单就 ...

  8. Java开发笔记(八十九)缓存字节I/O流

    文件输出流FileOutputStream跟FileWriter同样有个毛病,每次调用write方法都会直接写到磁盘,使得频繁的写操作性能极其低下.正如FileWriter搭上了缓存兄弟Buffere ...

  9. Java开发笔记(二十九)大整数BigInteger

    早期的编程语言为了节约计算机的内存,给数字变量定义了各种存储规格的数值类型,比如字节型byte只占用一个字节大小,短整型short占用两个字节大小,整型int占用四个字节大小,长整型long占用八个字 ...

随机推荐

  1. 自学 iOS - 三十天三十个 Swift 项目 第二天

    继续做仿造着别人的第二个 1.首先下载 一些字体 网上搜索 "造字工房" 2.把下载的相应字体文件放到工程之中,就Ok了 不多说 效果如下 可以下面这个方法 检索项目里面所有的字体 ...

  2. C#方法参数关键字

    一.params关键字 prams告诉函数的调用者,该函数的参数数量是可变,如果调用函数的参数标识了params关键字,那么我们可以使用逗号分割的参数或者一个数组来作为参数: 1.这里只能是数组,Li ...

  3. Farseer.net轻量级ORM开源框架 V1.8版本升级消息

    SHA-1: 775a93cf64df3f49c83cc4f4df346afd2075a68f * 发布V1.8.0修复:Oracle的SQL生成 在没有条件时,缺少Where关键字,导致无法分页修复 ...

  4. node.js入门之二

    NPM 1.NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种: 允许用户从NPM服务器下载别人编写的第三方包到本地使用. 允许用户从NPM ...

  5. leetcode_894. All Possible Full Binary Trees

    https://leetcode.com/problems/all-possible-full-binary-trees/ 给定节点个数,求所有可能二叉树,该二叉树所有节点要么有0个子节点要么有两个子 ...

  6. cookie和session的用法用途,执行流程,区别联系

    1.为什么要有cookie/session?在客户端浏览器向服务器发送请求,服务器做出响应之后,二者便会断开连接(一次会话结束).那么下次用户再来请求服务器,服务器没有任何办法去识别此用户是谁.比如w ...

  7. mysql通配符进行模糊查询

    在mysql数据库中,当我们需要模糊查询的时候 ,我们会使用到通配符. 首先我们来了解一下2个概念,一个是操作符,一个是通配符. 操作符 like就是SQL语句中的操作符,它的作用是指示在SQL语句后 ...

  8. python3.x Day4 模块!!

    json and pickle模块 用途是为了持久化信息,这种持久化方式可以和其他程序语言兼容,一般都支持json,json只能持久化数据,pickle是python特有的方式,可以持久化所有信息和数 ...

  9. c++基础_字符串对比

    #include <iostream> #include <string.h> #include <algorithm> using namespace std; ...

  10. cgi fastcgi php-cgi php-fpm

      参考: 摘至:http://www.cnblogs.com/thinksasa/p/4497567.html 详说fastcgi,php-fpm的区别:http://segmentfault.co ...