• 沒有找到結果。

Running a task in an executor periodically

在文檔中 Java 7 Concurrency Cookbook (頁 164-168)

The Executor framework provides the ThreadPoolExecutor class to execute concurrent tasks using a pool of threads that avoids you all the thread creation operations. When you send a task to the executor, according to its configuration, it executes the task as soon as possible. When it ends, the task is deleted from the executor and, if you want to execute them again, you have to send it again to the executor.

But the Executor framework provides the possibility of executing periodic tasks through the ScheduledThreadPoolExecutor class. In this recipe, you will learn how to use this functionality of that class to schedule a periodic task.

Getting ready

The example of this recipe has been implemented using the Eclipse IDE. If you use Eclipse or other IDE such as NetBeans, open it and create a new Java project.

How to do it...

Follow these steps to implement the example:

1. Create a class named Task and specify that it implements the Runnable interface.

public class Task implements Runnable {

2. Declare a private String attribute named name that will store the name of the task.

private String name;

3. Implement the constructor of the class that initializes that attribute.

public Task(String name) { this.name=name;

}

4. Implement the run() method. Write a message to the console with the actual date to verify that the task is executed within the specified period.

@Override

public String call() throws Exception {

System.out.printf("%s: Starting at : %s\n",name,new Date());

return "Hello, world";

}

5. Implement the main class of the example by creating a class named Main and implement the main() method in it.

public class Main {

public static void main(String[] args) { 6. Create ScheduledThreadPoolExecutor using the

newScheduledThreadPool() method of the Executors class. Pass the number 1 as the parameter to that method.

ScheduledExecutorService executor=Executors.

newScheduledThreadPool(1);

7. Write a message to the console with the actual date.

System.out.printf("Main: Starting at: %s\n",new Date());

8. Create a new Task object.

Task task=new Task("Task");

9. Send it to the executor using the scheduledAtFixRate() method. Use as parameters the task created earlier, the number one, the number two, and the constant TimeUnit.SECONDS. This method returns a ScheduledFuture object that you can use to control the status of the task.

ScheduledFuture<?> result=executor.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);

10. Create a loop with 10 steps to write the time remaining for the next execution of the task. In the loop, use the getDelay() method of the ScheduledFuture object to get the number of milliseconds until the next execution of the task.

for (int i=0; i<10; i++){

System.out.printf("Main: Delay: %d\n",result.

getDelay(TimeUnit.MILLISECONDS));

Sleep the thread during 500 milliseconds.

try {

TimeUnit.MILLISECONDS.sleep(500);

} catch (InterruptedException e) { e.printStackTrace();

} }

11. Finish the executor using the shutdown() method.

executor.shutdown();

12. Put the thread to sleep for 5 seconds to verify that the periodic tasks have finished.

try {

TimeUnit.SECONDS.sleep(5);

} catch (InterruptedException e) { e.printStackTrace();

}

13. Write a message to indicate the end of the program.

System.out.printf("Main: Finished at: %s\n",new Date());

How it works...

When you want to execute a periodic task using the Executor framework, you need a ScheduledExecutorService object. To create it (as with every executor), Java recommends the use of the Executors class. This class works as a factory of executor objects. In this case, you should use the newScheduledThreadPool() method to create a ScheduledExecutorService object. That method receives as a parameter the number of threads of the pool. As you have only one task in this example, you have passed the value 1 as a parameter.

Once you have the executor needed to execute a periodic task, you send the task to the executor. You have used the scheduledAtFixedRate() method. This method accepts four parameters: the task you want to execute periodically, the delay of time until the first execution of the task, the period between two executions, and the time unit of the second and third parameters. It's a constant of the TimeUnit class. The TimeUnit class is an enumeration with the following constants: DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, and SECONDS.

An important point to consider is that the period between two executions is the period of time between these two executions that begins. If you have a periodic task that takes 5 sceconds to execute and you put a period of 3 seconds, you will have two instances of the task executing at a time.

The method scheduleAtFixedRate() returns a ScheduledFuture object, which extends the Future interface, with methods to work with scheduled tasks. ScheduledFuture is a parameterized interface. In this example, as your task is a Runnable object that is not parameterized, you have to parameterize them with the ? symbol as a parameter.

You have used one method of the ScheduledFuture interface. The getDelay() method returns the time until the next execution of the task. This method receives a TimeUnit constant with the time unit in which you want to receive the results.

The following screenshot shows the output of an execution of the example:

You can see the task executing every 2 seconds (denoted with Task: prefix) and the delay written in the console every 500 milliseconds. That's how long the main thread has been put to sleep. When you shut down the executor, the scheduled task ends its execution and you don't see more messages in the console.

There's more...

ScheduledThreadPoolExecutor provides other methods to schedule periodic tasks.

It is the scheduleWithFixedRate() method. It has the same parameters as the scheduledAtFixedRate() method, but there is a difference worth noticing. In the scheduledAtFixedRate() method, the third parameter determines the period of time between the starting of two executions. In the scheduledWithFixedRate() method, parameter determines the period of time between the end of an execution of the task and the beginning of the next execution.

You can also configure the behavior of an instance of the ScheduledThreadPoolExecutor class with the shutdown() method. The default behavior is that the scheduled

tasks finish when you call that method. You can change this behavior using the setContinueExistingPeriodicTasksAfterShutdownPolicy() method of the ScheduledThreadPoolExecutor class with a true value. The periodic tasks won't finish upon calling the shutdown() method.

See also

f The Creating a thread executor recipe in Chapter 4, Thread Executors

f The Running a task in an executor after a delay recipe in Chapter 4, Thread Executors

在文檔中 Java 7 Concurrency Cookbook (頁 164-168)