Java开发笔记(九十九)定时器与定时任务

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

1
2
3
4
5
6
7
8
9
10
11
// 定义一个用于计数的定时任务
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方法为例,通过该方法延迟若干时间后启动定时任务的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 测试只跑一次的定时器调度
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方法改为固定间隔启动定时任务的话,只需添加第三个参数就好了,调用代码片段示例如下:

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

 

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

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

 

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

1
2
3
4
5
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开发笔记(序)章节目录

posted @   pinlantu  阅读(1059)  评论(0编辑  收藏  举报
编辑推荐:
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· SQL Server 内存占用高分析
· .NET Core GC计划阶段(plan_phase)底层原理浅谈
· .NET开发智能桌面机器人:用.NET IoT库编写驱动控制两个屏幕
· 用纯.NET开发并制作一个智能桌面机器人:从.NET IoT入门开始
阅读排行:
· 我干了两个月的大项目,开源了!
· 推荐一款非常好用的在线 SSH 管理工具
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· 千万级的大表,如何做性能调优?
· .NET周刊【1月第1期 2025-01-05】
点击右上角即可分享
微信分享提示