Java Timer简介

486 阅读3分钟
原文链接: www.ripjava.com

1 概述

在Java中如果需要定时执行某些任务,我们可以使用java.util包提供Timer和TimerTask,在后台线程中调度任务。 简单来说,TimerTask是要执行的任务,Timer是调度程序。

2 只执行一次的定时任务

首先我们使用Timer的来运行一个定时任务:

    @Test
    public void testTimer_SchedulingTaskOnce() throws Exception {
        TimerTask task = new TimerTask() {
            public void run() {
                System.out.println("Task performed on: " + new Date() + "n" +
                        "Thread's name: " + Thread.currentThread().getName());
            }
        };
        Timer timer = new Timer("Timer");
        long delay = 1000L;
        timer.schedule(task, delay);
        Thread.sleep(delay * 2);
    }

注意,我们要添加一个Thread.sleep(delay * 2),允许Timer的线程在Junit测试停止执行之前运行任务。

3. 重复执行定时任务

下面,給大家介绍如何定义一个以预定义的时间间隔运行的任务。

我们将使用scheduleAtFixedRate(repeatedTask,delay,period)方法。

它在指定的延迟(delay)之后,间隔指定的时间段(period),重复执行的任务。

    @Test
    public void testTimer_SchedulingRepeatedTask() throws InterruptedException{
        TimerTask repeatedTask = new TimerTask() {
            public void run() {
                System.out.println("Task performed on " + new Date());
            }
        };
        Timer timer = new Timer("Timer");

        long delay  = 1000L;
        long period = 1000L;
        timer.scheduleAtFixedRate(repeatedTask, delay, period);
        Thread.sleep(delay * 10);
    }

如果执行因为一些原因(例如垃圾收集或其他后台活动)而延迟,则会快速连续执行两次或更多次执行以“赶上”正常的执行次数。

3.1 定义每天执行的任务

下面,給大家介绍如何定义一个每天都执行的任务:

    @Test
    public void testTimer_SchedulingDailyTask() {
        TimerTask repeatedTask = new TimerTask() {
            public void run() {
                System.out.println("Task performed on " + new Date());
            }
        };
        Timer timer = new Timer("Timer");

        long delay = 1000L;
        long period = 1000L * 60L * 60L * 24L;
        timer.scheduleAtFixedRate(repeatedTask, delay, period);
    }

这个就不写Thread.sleep了,等不起。

4.取消定时器和定时任务

可以通过以下几种方式取消任务的执行:

4.1 取消运行中的TimerTask

TimerTask本身的实现类里,通过在run()方法的中调用TimerTask.cancel()方法:

    @Test
    public void testTimer_CancelingTimerTask()
            throws InterruptedException {
        TimerTask task = new TimerTask() {
            public void run() {
                System.out.println("Task performed on " + new Date());
                cancel();
            }
        };
        Timer timer = new Timer("Timer");

        timer.scheduleAtFixedRate(task, 1000L, 1000L);

        Thread.sleep(1000L * 10);
    }

执行上面的代码,会发现TimerTask只被执行了一次。

4.2 取消定时器

通过在Timer对象上调用Timer.cancel()方法:

    @Test
    public void testTimer_CancelingTimer()
      throws InterruptedException {
        TimerTask task = new TimerTask() {
            public void run() {
                System.out.println("Task performed on " + new Date());
            }
        };
        Timer timer = new Timer("Timer");

        timer.scheduleAtFixedRate(task, 1000L, 1000L);
        Thread.sleep(1000L * 1);
        timer.cancel();
        Thread.sleep(1000L * 9);
    }

执行上面的代码,会发现TimerTask也只被执行了一次。

4.3 在运行中停止TimerTask的Thread

还可以在任务的run方法中停止线程,从而取消整个任务: 注意运行实现中的TODO指令 - 为了运行这个简单的例子,我们需要实际停止线程。

    @Test
    public void testTimer_StoppingThread()
            throws InterruptedException {
        TimerTask task = new TimerTask() {
            public void run() {
                System.out.println("Task performed on " + new Date());
                // TODO: 假如在这里停止
                Thread.currentThread().stop();
            }
        };
        Timer timer = new Timer("Timer");

        timer.scheduleAtFixedRate(task, 1000L, 1000L);

        Thread.sleep(1000L * 2);
    }

在实际的自定义线程实现中,一般会支持停止线程。

这里为了简单使用Thread类的stop方法,这个方法已经被废弃了。

5.Timer VS ExecutorService

还可以利用ExecutorService来执行定时器任务,而不是使用定时器。

以下是如何以指定间隔运行重复任务的示例:

    @Test
    public void testTimer_SchedulingRepeatedTask_ByScheduledExecutorService() throws InterruptedException{
        TimerTask repeatedTask = new TimerTask() {
            public void run() {
                System.out.println("Task performed on " + new Date());
            }
        };
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        long delay = 1000L;
        long period = 1000L;
        executor.scheduleAtFixedRate(repeatedTask, delay, period, TimeUnit.MILLISECONDS);
        Thread.sleep(delay + period * 3);
        executor.shutdown();
    }

那么Timer和ExecutorService之间的主要区别是什么:

  • 定时器可以对系统时钟的变化敏感; ScheduledThreadPoolExecutor不是
  • Timer只有一个执行线程; ScheduledThreadPoolExecutor可以配置任意数量的线程
  • 在TimerTask内部抛出的运行时异常会杀死该线程,因此整个任务将无法再运行;
  • 使用ScheduledThreadExecutor, 当前任务被取消停止,其余任务还将继续运行

6 结论

本教程说明了使用Java内置的简单而灵活的Timer和TimerTask基础结构的许多方法,可以快速实现调度任务。

如果你需要更复杂和完整的解决方案,在Java的世界里还有Quartz等开源框架。