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.
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.
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( )); } }
Here's some output from this class:
[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
This code is actually every bit as simple as it looks. You obtain a new 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.
...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:
[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
You'll run into this with all your scheduling programs, so you might want to keep this in mind.
NOTE
For a lengthy discussion on the "final" keyword, you might want to check out Hardcore Java (O'Reilly).