10.9 Scheduling Tasks
Along with the ability to separate
the logic of a task (in a Callable, Runnable, or Thread object) from its execution, Tiger allows you to schedule execution at specific times.
10.9.1 How do I do that?
Earlier, you saw how the Executors class was used to obtain several
ExecutorServices. Two methods that were brushed over were
newScheduledThreadPool( ) and newSingleThreadScheduledExecutor( ).
Both of these return instances of ScheduledExecutorService, which adds
several features to the basic ExecutorService interface. The simplest to
use is scheduleAtFixedRate( ), shown in action in Example 10-6.
Example 10-6. Using scheduled tasks
Here's some output from this class:
package com.oreilly.tiger.ch10;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import static java.util.concurrent.TimeUnit.*;
public class ScheduleTester {
public static void main(String[] args) {
// Get the scheduler
ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor( );
// Get a handle, starting now, with a 10 second delay
final ScheduledFuture<?> timeHandle =
scheduler.scheduleAtFixedRate(new TimePrinter(System.out), 0, 10, SECONDS);
// Schedule the event, and run for 1 hour (60 * 60 seconds)
scheduler.schedule(new Runnable( ) {
public void run( ) {
timeHandle.cancel(false);
}
}, 60*60, SECONDS);
}
}
}
class TimePrinter implements Runnable {
private PrintStream out;
public TimePrinter(PrintStream out) {
this.out = out;
}
public void run( ) {
out.printf("Current time: %tr%n", new Date( ));
}
}
This code is actually every bit as simple as it looks. You obtain a new
[echo] Running ScheduleTester...
[java] Current time: 09:17:04 AM
[java] Current time: 09:17:14 AM
[java] Current time: 09:17:24 AM
[java] Current time: 09:17:34 AM
ScheduledExecutorService from Executors, and use the
scheduleAtFixedRate( ) method to initiate a task. This method takes the
task to run (either a Runnable or Callable implementation, then takes a delay (how long to wait before beginning execution), the period between
executions, and the TimeUnit that these durations are expressed in. In
the example, the TimePrinter thread is set up to run immediately, and
then every 10 seconds thereafter.The scheduleAtFixedRate( ) method returns a ScheduledFuture
instance, which extends both the Future interface (detailed in Using Callable Objects), and the Delayed interface, which I haven't mentioned
yet. Delayed is used for objects that are acted upon after a certain delay.
Its getDelay( ) method, in the context of a ScheduledFuture, allows you to determine how much time is left before subsequent executions of your
task.The final piece of the puzzle is the schedule( ) invocation, which at first
glance may confuse you. schedule( ) works just like
scheduleAtFixedRate( ), but sets up a single execution, rather than multiple
ones. In the example, it's used to initiate a thread that will cancel
the execution of TimePrinter, after an hour (60*60 seconds). This is an
important part of schedulingotherwise the thread printing the date
would run on infinitely. It also makes the ScheduledFuture object important
it provides the only means of canceling the task's execution.
10.9.2 What about...
...that final declaration on the ScheduledFuture object? Because the
inner class passed to the scheduler has to access the Future object,
you've got to mark it as final. Otherwise, you'll get this error:
You'll run into this with all your scheduling programs, so you might want
[javac] code\src\com\oreilly\tiger\ch10\ScheduleTester.java:26:
local variable timeHandle is accessed from within inner class;
needs to be declared final
[javac] timeHandle.cancel(true);
[javac] ^
[javac] 1 error
to keep this in mind.NOTEFor a lengthy discussion on the "final" keyword, you might want to
check out Hardcore Java (O'Reilly).