Timer & TimerTask 与 Java 中的 Thread + 睡眠

我在这里发现了类似的问题,但是没有令我满意的答案

我有一项任务需要定期完成(比如说每隔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。如果我们出于某种原因将系统时间回滚到上午10:00,那么 Timer 将停止执行任务直到上午11:00,而 Thread.sleep ()方法将毫无阻碍地继续执行任务。这可能是决定在这两者之间使用什么的主要决策者。

102534 次浏览

The advantage of TimerTask is that it expresses your intention much better (i.e. code readability), and it already has the cancel() feature implemented.

Note that it can be written in a shorter form as well as your own example:

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

Timer/TimerTask also takes into account the execution time of your task, so it will be a bit more accurate. And it deals better with multithreading issues (such as avoiding deadlocks etc.). And of course it is usually better to use well-tested standard code instead of some homemade solution.

There's one crucial argument against managing this task using Java threads and sleep method. You are using while(true) to stay indefinitely in the loop and hibernate the thread by putting to sleep. What if NewUploadServer.getInstance().checkAndUploadFiles(); takes up some synchronized resources. Other threads will be unable to access these resources, starvation may happen which can slow down your whole application. These kinds of errors are hard to diagnose and it's a good idea to prevent their existance.

The other aproach triggers the execution of the code that matters to you, i.e. NewUploadServer.getInstance().checkAndUploadFiles(); by calling the run() method of your TimerTask while letting other threads using the resources in the meanwhile.

I don't know why but a program that I was writing was using Timers and it's heap size was increasing constantly, once I changed it to Thread/sleep problem solved.

If you thread get exception and gets killed, that is a problem. But TimerTask will take care of it. It will run irrespective of failure in previous run.

I think I understand your issue, I am seeing something very similar. I have timers that are recurring, some every 30 minutes and some every couple of days. From what I read and the comments I see, it looks like garbage collection will never run because all the task are never complete. I would think that garbage collection would run when a timer is in sleep, but I am not seeing it and according to the documentation it does not.

I think that spawning new threads completes and allow garbage collection.

Someone please prove me wrong, rewriting what I inherited is going to be a pain.

From the Timer documentation:

Java 5.0 introduced the java.util.concurrent package and one of the concurrency utilities therein is the ScheduledThreadPoolExecutor which is a thread pool for repeatedly executing tasks at a given rate or delay. It is effectively a more versatile replacement for the Timer/TimerTask combination, as it allows multiple service threads, accepts various time units, and doesn't require subclassing TimerTask (just implement Runnable). Configuring ScheduledThreadPoolExecutor with one thread makes it equivalent to Timer.

So Prefer ScheduledThreadExecutor instead of Timer:

  • Timer uses single background thread that is used to execute all of the timer's tasks, sequentially. So tasks should complete quickly else it will delay the execution of subsequent tasks. But in case of ScheduledThreadPoolExecutor we can configure any number of threads and can also have full control by providing ThreadFactory.
  • Timer can be sensitive to system clock as it makes use of Object.wait(long) method. But ScheduledThreadPoolExecutor is not.
  • Runtime exceptions thrown in TimerTask will kill that particular thread, thus making Timer dead where as we can handle that in ScheduledThreadPoolExecutor so that the other tasks are not impacted.
  • Timer provides cancel method to terminate the timer and discard any scheduled tasks, however it doesn’t interfere with the currently executing task and let it finish. But if timer is running as daemon thread then whether we cancel it or not, it will terminate as soon as all the user threads are finished executing.

Timer vs Thread.sleep

Timer makes use of Object.wait and it is different from Thread.sleep

  1. A waiting (wait) thread can be notified (using notify) by another thread but a sleeping one cannot be, it can only be interrupted.
  2. A wait (and notify) must happen in a block synchronized on the monitor object whereas sleep does not.
  3. While sleeping does not release the lock, waiting will release the lock for the object wait is called upon.