Timer和TimerTask与Java中的线程+睡眠


102

我在这里发现了类似的问题,但没有满意的答案。所以再改一下这个问题-

我有一项需要定期执行的任务(例如间隔1分钟)。与创建一个带有睡眠无限循环的新线程相比,使用Timertask&Timer这样做有什么好处?

使用timertask-的代码段

TimerTask uploadCheckerTimerTask = new TimerTask(){

 public void run() {
  NewUploadServer.getInstance().checkAndUploadFiles();
 }
};

Timer uploadCheckerTimer = new Timer(true);
uploadCheckerTimer.scheduleAtFixedRate(uploadCheckerTimerTask, 0, 60 * 1000);

使用Thread和sleep-的代码段

Thread t = new Thread(){
 public void run() {
  while(true) {
   NewUploadServer.getInstance().checkAndUploadFiles();
   Thread.sleep(60 * 1000);
  }
 }
};
t.start();

如果逻辑执行花费的时间超过间隔时间,我真的不必担心是否会错过某些周期。

请对此发表评论。

更新:
最近,我发现使用Timer与Thread.sleep()之间还有另一个区别。假设当前系统时间为11:00 AM。如果由于某种原因将系统时间回滚到10:00 AM,则Timer将停止执行任务,直到达到11:00 AM,而Thread.sleep()方法将继续执行该任务而没有任何阻碍。这可能是决定这两者之间使用什么的主要决策者。


21
程序点:尽管您的观点仍然成立,但Timer和TimerTask已过时,并且已由ExecutorService进行了有效替换。
skaffman

感谢您的提示,我决定使用ExecutorService :)
Keshav

谢谢大家的回答,肯定给了我更多的理解!
Keshav

6
计时器不是过时的,当仅需要一个线程时,首选计时器。(java.sun.com/javase/6/docs/api/java/util/Timer.html
贾斯汀

2
Timer和TimerTask在不存在ExecutorService的JME环境中仍然有用(因为基于JME Java 1.3 ...)。
スーパーファミコン

Answers:


67

TimerTask的优点是它可以更好地表达您的意图(即代码可读性),并且已经实现了cancel()功能。

请注意,它可以用较短的格式以及您自己的示例编写:

Timer uploadCheckerTimer = new Timer(true);
uploadCheckerTimer.scheduleAtFixedRate(
    new TimerTask() {
      public void run() { NewUploadServer.getInstance().checkAndUploadFiles(); }
    }, 0, 60 * 1000);

如果将计时器用于每天的2个特定时间(晚上9点和上午9点),则如何给出值?在上面的代码... @Zed上?
gumuruh '16

12

Timer / TimerTask还考虑了任务的执行时间,因此它将更加准确。而且它可以更好地处理多线程问题(例如避免死锁等)。当然,通常最好使用经过测试的标准代码,而不是某些自制的解决方案。


5

我不知道为什么,但是我正在编写的程序正在使用Timers,并且一旦我将其更改为Thread / sleep问题,它的堆大小就会不断增加。


9
计时器会创建一个不断更新的任务队列。完成计时器后,可能不会立即对其进行垃圾回收。因此,创建更多计时器只会将更多对象添加到堆中。Thread.sleep()仅暂停线程,因此内存开销将非常低。
Darryl Gerrow 2013年

4

如果线程获取异常并被杀死,那就是一个问题。但是TimerTask会照顾好它。它将与之前的运行无关地运行。


4

Timer 文档中

Java 5.0引入了java.util.concurrent包,其中的并发实用程序之一是ScheduledThreadPoolExecutor,它是一个线程池,用于以给定的速率或延迟重复执行任务。实际上,它是Timer / TimerTask组合的更通用的替代品,因为它允许多个服务线程,接受各种时间单位,并且不需要子类化TimerTask(只需实现Runnable)。使用一个线程配置ScheduledThreadPoolExecutor使其等同于Timer。

因此,请优先ScheduledThreadExecutor选择Timer

  • Timer使用单个后台线程,该后台线程用于依次执行所有计时器任务。因此,任务应快速完成,否则会延迟后续任务的执行。但是,在这种情况下,ScheduledThreadPoolExecutor我们可以配置任意数量的线程,并且还可以通过提供进行完全控制ThreadFactory
  • Timer使用Object.wait(long)方法可能会对系统时钟敏感。但是ScheduledThreadPoolExecutor不是。
  • 在TimerTask中抛出的运行时异常将杀死该特定线程,从而使Timer死于我们可以处理的地方,ScheduledThreadPoolExecutor从而不会影响其他任务。
  • Timer提供了cancel一种方法来终止计时器并丢弃所有计划的任务,但是它不会干扰当前正在执行的任务并使其完成。但是,如果timer作为守护程序线程运行,则无论我们是否取消它,它都会在所有用户线程完成执行后立即终止。

计时器与Thread.sleep

计时器利用,Object.wait它不同于Thread.sleep

  1. wait可以notify通过另一个线程(使用)来通知正在等待的()线程,但不能通知正在休眠的线程,只能将其中断。
  2. 等待(和通知)必须在监视对象上同步的块中发生,而睡眠则不需要。
  3. 当睡眠时不释放锁,等待将释放锁,以等待对象等待。

非常有用的信息,此外,在无限循环中使用Thread.sleep可以在较低的时间延迟内导致较高的CPU使用率。
阿米尔·佛

3

有一个关键论点反对使用Java线程和sleep方法来管理此任务。您while(true)习惯于无限期地停留在循环中并通过进入睡眠状态来休眠线程。如果NewUploadServer.getInstance().checkAndUploadFiles();占用一些同步资源怎么办?其他线程将无法访问这些资源,可能会发生饥饿,这可能会减慢整个应用程序的速度。这些类型的错误很难诊断,因此防止它们的存在是一个好主意。

另一个方法触发了对您重要的代码的执行,即,NewUploadServer.getInstance().checkAndUploadFiles();通过调用run()您的方法,TimerTask同时让其他线程使用资源。


16
我不明白这个说法。两种选择都从线程启动相同的方法,两种选择都在等待执行时在线程中休眠。两种选择之间没有饥饿差异。
satur9nine 2011年

2

我想我了解您的问题,我看到的情况非常相似。我有一些重复的计时器,有些每30分钟一次,有些每几天一次。从我阅读的内容和看到的评论看来,垃圾回收将永远不会运行,因为所有任务都不会完成。我认为当计时器处于睡眠状态时,垃圾收集将运行,但是我没有看到它,并且根据文档,它没有。

我认为产生新线程完成并允许垃圾回收。

有人请证明我错了,重写我继承的东西会很痛苦。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.