- Introspecting my wandering mind - http://rajandesai.com/blog -

Java Tip: How to use Java Timer to synchronize multiple repeating tasks

Recently I had to implement a service that involved multiple nodes in a cluster to aggregate the data for certain period of time and report it back to the central server. The central server, then aggregated results provided by all the nodes in the cluster. This required all the nodes to align properly when reporting the aggregate data to the central.

For example, if the system was configured to aggregate data for 10 minutes, each node in the cluster was expected to send the aggregate data, every 10th minute beginning at the top of the hour. In this case, the central server was expecting data from all the nodes at 10:00 am, 10:10 am, 10:20 am, 10:30 am and so on. The node started at 10:05:15 am was expected to report its aggregate data at 10:10:00 am and every 10th minutes after that (instead of 10 minutes after the node is started and every 10 minutes after that).

The java.util.Timer [1] and java.util.TimerTask [2] utility clases can be very handy to implement such a functionality. These classes can be used to execute tasks periodically or just once. The scheduleAtFixedRate [3] method on the Timer class can be used to delay the task to a specific time.

The following example shows how Timer and TimerTask utilities can be used to implement the functionality explained above.

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TestTimer extends TimerTask {
static DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

@Override
public void run() {
java.util.Date date = new java.util.Date();
System.out.println("Testing Timer @ " + dateFormat.format(date));
}

public static void main (String... arguments ) {
int frequency = 10;            //Timer frequency in minutes.
TimerTask testTask  = new TestTimer();

Timer timer = new Timer();

Calendar startTime = Calendar.getInstance();
int minutes = startTime.get(Calendar.MINUTE);
System.out.println(startTime.get(Calendar.HOUR_OF_DAY) + ":" +
minutes + ":" +
startTime.get(Calendar.HOUR_OF_DAY));

//In order to align timer tasks, we have to ensure that timer for this
//thread is started at correct time.
//For example, if frequency was set to 10 minutes and if this task
//was started at 10:05:02 AM, first task should kick off at 10:10:00 AM
//(and every 10 minutes after that).
int minutesPassedSinceLastTask = minutes % frequency;
int minutesToNextTask = frequency - minutesPassedSinceLastTask;
System.out.println("minutesPassedSinceLastTique : " + minutesPassedSinceLastTask);
System.out.println("minutesToNextTique : " + minutesToNextTask);

//Adjust the start time of the task.
startTime.add(Calendar.MINUTE, minutesToNextTask);

//This ensure that we are always on the 0th second
startTime.set(Calendar.SECOND, 0);

System.out.println("Start time for the timer : " + dateFormat.format(startTime.getTime()));

//Schedule the timer to run the task at the adjusted start time,
//repeating it after every "frequency" minutes.
timer.scheduleAtFixedRate(testTask, startTime.getTime(), frequency * 60 * 1000);
System.out.println("============== Task Execution Output ===============");
}
}

Running this code produces the following output:

10:27:10
minutesPassedSinceLastTask : 7
minutesToNextTask : 3
Start time for the timer : 2010/03/24 10:30:00
============== Task Execution Output ===============
Testing Timer @ 2010/03/24 10:30:00
Testing Timer @ 2010/03/24 10:40:00
Testing Timer @ 2010/03/24 10:50:00
Testing Timer @ 2010/03/24 11:00:00