如何安排任务定期运行?

87

我在尝试编写一个定时任务,然后得到了下面的代码。

import java.util.*;

class Task extends TimerTask {


    int count = 1;

    // run is a abstract method that defines task performed at scheduled time.
    public void run() {
        System.out.println(count+" : Mahendra Singh");
        count++;
    }
}

class TaskScheduling {

   public static void main(String[] args) {
       Timer timer = new Timer();


       // Schedule to run after every 3 second(3000 millisecond)
       timer.schedule( new Task(), 3000);   
   }
}

我的输出:

1  :  Mahendra Singh

我期望编译器每3秒打印一系列Mahendra Singh,但是尽管等待了大约15分钟,我只得到了一个输出...我该如何解决这个问题?


Quartz调度程序是帮助Java应用程序在指定日期和时间安排任务/作业运行的工具可查看完整示例 - Tell Me How
1
这些类已被Executors框架所取代。请参阅Timer的JavaDoc第7段。 - Basil Bourque
4个回答

89

ScheduledExecutorService 相对于 Timer 的优势

我想给您提供一种替代使用 Timer 的方式 - ScheduledThreadPoolExecutor,它是 ScheduledExecutorService 接口的实现。根据《Java并发编程实战》书中的介绍,它具有以下几个优点:

Timer 只会创建一个线程来执行计时器任务。如果一个定时任务需要运行的时间太长,其他的 TimerTask 的定时精度就会受到影响。例如,如果一个定时任务被安排每10毫秒运行一次,而另一个 Timer-Task 需要40毫秒才能运行完成,则这个定时任务要么(取决于它是否以固定速率或固定延迟为安排)在长时间运行的任务完成后立即连续调用4次,要么完全“错过”4次调用。Scheduled thread pools 可以通过让您提供多个线程来执行延迟和周期性任务来解决此限制。

另一个问题是,如果一个 TimerTask 抛出未经检查的异常,则 Timer 的行为会表现得很差。这种情况也被称为“线程泄漏”。

Timer 线程无法捕获异常,因此从 TimerTask 抛出的未检查异常会终止定时器线程。在这种情况下,Timer 也不会恢复线程;相反,它错误地假设整个 Timer 已被取消。在这种情况下,已安排但尚未执行的 TimerTask 将永远不会运行,而新任务也无法被安排。

如果您需要构建自己的调度服务,另一个建议是使用 DelayQueue。它是一个BlockingQueue实现,提供了ScheduledThreadPoolExecutor调度功能。 DelayQueue 管理一个 Delayed 对象集合。每个延迟对象都有一个与之关联的延迟时间:只有当延迟时间到期时才能从 DelayQueue 中获取元素。这些对象按照与其延迟时间相关的时间顺序从 DelayQueue 返回。


4
定时器的另一个问题是当系统时钟发生变化时,它会影响定时器的正常运行。 - Rajath

83

使用timer.scheduleAtFixedRate

public void scheduleAtFixedRate(TimerTask task,
                                long delay,
                                long period)

在指定的延迟后,安排指定任务以重复固定速率执行。随后的执行大致按照指定周期分开,每个执行都相对于初始执行的计划执行时间进行调度。如果由于垃圾回收或其他后台活动等原因导致执行延迟,则会连续发生两次或更多次执行以“赶上”。从长远来看,执行频率将完全是指定周期的倒数(假设Object.wait(long)的系统时钟准确无误)。

固定速率的执行适用于对绝对时间敏感的重复性活动,例如在整点每小时响铃或在特定时间每天运行预定维护。它也适用于执行固定数量的操作所需的总时间很重要的重复性活动,例如每秒滴答一次十秒倒计时器。最后,固定速率执行适用于调度必须相互同步的多个重复计时器任务。

参数:

  • task - 要安排的任务。
  • delay - 在执行任务之前的毫秒延迟时间。
  • period - 相继任务执行之间的毫秒时间。

抛出:

  • IllegalArgumentException - 如果延迟为负数,或者延迟+System.currentTimeMillis()为负数。
  • IllegalStateException - 如果任务已经安排或取消安排,计时器已取消,或计时器线程终止。

4
scheduleAtFixedRate并不能解决他仅获取一次输出的问题。 - James A. N. Stauffer
1
@JamesA.N.Stauffer,他需要重复输出,而不是一次性的。 - st0le
3
可以通过添加第三个参数来实现重复,而不需要改变方法。 - James A. N. Stauffer
2
@st0le,考虑以下类比。提问者说“天空是蓝色的”,而你说“不,草是绿色的”。- 如果他的问题是“什么是固定利率重复”,那么你的答案就是理想的。 - tony9099
如果您希望它在特定的上限和下限范围内是随机速率呢? - Hack-R

15
public void schedule(TimerTask task,long delay)

安排指定的任务延迟执行。

public void schedule(TimerTask task, long delay, long period)

在指定延迟时间后,安排特定任务进行重复固定延迟执行。随后的执行大约以指定时间间隔分隔。


2
timer.scheduleAtFixedRate( new Task(), 1000,3000); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接