An article to understand Timer

Keywords: Java Concurrent Programming

preface

This article belongs to the column "100 problems to solve Java concurrency". This column is original by the author. Please indicate the source of quotation. Please help point out the deficiencies and errors in the comment area. Thank you!

Please refer to table of contents and references for this column 100 problems to solve Java concurrency

text

What is Timer?

The Timer class is responsible for managing delayed tasks ("execute the task after 100ms") and periodic tasks ("execute the task every 10ms").

Two major defects of Timer

However, Timer has some defects, so you should consider using ScheduledThreadPoolExecutor instead.

You can create objects of this class through the constructor of ScheduledThreadPoolExecutor or the newScheduledThreadpool factory method.

Timer will only create one thread when executing all scheduled tasks.

If the execution time of a task is too long, the timing accuracy of other timertasks will be destroyed.

For example, a periodic TimerTask needs to be executed every 10ms, while another TimerTask needs to be executed for 40ms, then this periodic task either calls 4 times quickly and continuously after the execution of 40ms task, or completely "loses" 4 calls (depending on whether it is scheduled based on fixed rate or fixed delay).

Thread pool can make up for this defect. It can provide multiple threads to execute delayed tasks and periodic tasks.

Another problem with Timer is that if TimerTask throws an unchecked exception, Timer will show bad behavior.

The Timer thread does not catch exceptions, so when TimerTask throws an unchecked exception, the timing thread will be terminated.

In this case, the Timer will not resume the execution of the thread, but will mistakenly think that the whole Timer has been cancelled.

Therefore, TimerTask that has been scheduled but not yet executed will not be executed, and new tasks cannot be scheduled. (this problem is called "Thread Leakage")

Code example

The following code example shows why this problem occurs in the Timer and how to cause problems for callers trying to submit TimerTask.

import java.util.Timer;
import java.util.TimerTask;

import static java.util.concurrent.TimeUnit.SECONDS;

public class OutOfTime {
	public static void main(String[] args) throws Exception {
		Timer timer = new Timer();
		timer.schedule(new Throwtask(), 1);
		SECONDS.sleep(1);
		timer.schedule(new Throwtask(), 1);
		SECONDS.sleep(5);
	}

	static class Throwtask extends TimerTask {

		public void run() {
			throw new RuntimeException();
		}
	}
}

You may think that the program will exit after running for 6 seconds, but the actual situation is that it ends after running for 1 second, and an exception message "Timer already cancelled" is thrown.

Exception in thread "Timer-0" java.lang.RuntimeException
	at com.shockang.study.java.concurrent.scheduler.OutOfTime$Throwtask.run(OutOfTime.java:20)
	at java.util.TimerThread.mainLoop(Timer.java:555)
	at java.util.TimerThread.run(Timer.java:505)
Exception in thread "main" java.lang.IllegalStateException: Timer already cancelled.
	at java.util.Timer.sched(Timer.java:397)
	at java.util.Timer.schedule(Timer.java:193)
	at com.shockang.study.java.concurrent.scheduler.OutOfTime.main(OutOfTime.java:13)

The ScheduledThreadPoolExecutor can correctly handle these tasks that exhibit incorrect behavior.

In Java 5.0 or later jdks, timers will be rarely used.

If you want to build your own scheduling service, you can use DelayQueue, which implements BlockingQueue and provides scheduling function for ScheduledThreadPoolExecutor.

Delayqueue manages a set of Delayed objects.

Each Delayed object has a corresponding delay time: in the delayqueue, only after an element is overdue, you can perform the take operation from the delayqueue.

Objects returned from DelayQueue are sorted according to their delay time.

Posted by Sassci on Wed, 10 Nov 2021 14:50:37 -0800