e-Zest members share technology ideas to foster digital transformation.

The Executor Framework – Part II

Written by Madhura Oak | Feb 18, 2013 11:31:21 AM

In the Part I of Executor Framework blog post series, I had written how it can be used to create a single worker thread for sequential execution of tasks and create a thread pool of fixed number of threads. In this blog post, I’m writing about the following uses of Executor framework:

  1. Creating asynchronous task
  2. Creating cancellable Runnable task
  3. Creating Runnable task with a timeout

Creating asynchronous task

The submit methods of ExecutorService allow tasks to be submitted for asynchronous execution. There are three submit methods, of which two are explained in this blog post. All the submit methods return a Future which is used to get the result of asynchronous task execution.

The examples used in this blog post (Listing 2 and 4) create asynchronous tasks.

Creating cancellable Runnable task

The submit(Runnable task) method of ExecutorService returns a Future<?> representation of the task. The cancel(boolean mayInterruptIfRunning) method of Future is used to attempt cancellation of the task by interrupting the thread running it. The thread in RUNNABLE state cannot be interrupted. Only the threads in WAITING or TIMED_WAITING states can be interrupted.

If the task is not started when cancel(boolean mayInterruptIfRunning) method is called, it will never be executed. If the task execution is started then this method decides whether to interrupt the thread running the task depending on the boolean argument passed to it. An InterruptedException is thrown when a thread is interrupted. The code should take care of stopping the task when this exception is handled as shown in Listing 1. If the cancel method is able to cancel the task then it returns true. If the task is already completed or already cancelled or could not be cancelled then it returns false.

The isCancelled() method of Future returns true if the task is cancelled before its normal completion.

The isDone() method of Future returns true if the task is completed normally or upon cancellation. It returns false if the task is under execution.

The get() method of Future waits until the task is complete and returns the result after completion. It returns null for Runnable tasks unless the return value is specified by calling submit(Runnable task, T result) method. The submit(Runnable task, T result) method returns Future<T>. If the thread waiting to get the result of this method is interrupted, the execution of asynchronous task stops and InterruptedException is thrown by this method. If the task throws an exception during execution, the ExecutionException is thrown by the get() method.

Listing 1 shows a Runnable task which prints numbers after a delay of 0.5 seconds. Note the use of boolean property stop. This task uses Thread.sleep method for the thread to go in waiting state so that it can be interrupted. After the thread running this task is interrupted, its execution is stopped with the help of this property.

public class PrintNumberWithDelay implements Runnable {
    private boolean stop;

    public void run() {
        for(int i=1; !stop && i <= 5; i++) {
            try {
                Thread.sleep(500);
                System.out.println(i);
            }
            catch (InterruptedException e) {
                stop = true;
            }
        }
    }
}

Listing 1. Runnable task to print numbers with delay

Listing 2 has a code which creates three tasks and submits them for execution to a pool of two threads. After the first task is completed, the remaining tasks are cancelled.

PrintNumberWithDelay task1 = new PrintNumberWithDelay();
PrintNumberWithDelay task2 = new PrintNumberWithDelay();
PrintNumberWithDelay task3 = new PrintNumberWithDelay();

//create a fixed sized thread pool with 2 threads
ExecutorService service = Executors.newFixedThreadPool(2);

//submit tasks for execution
Future<?> future1 = service.submit(task1);
Future<?> future2 = service.submit(task2);
Future<?> future3 = service.submit(task3);

//wait for the first task to complete
try {
    future1.get();
}
catch (InterruptedException e) {
    e.printStackTrace();
}
catch (ExecutionException e) {
    e.printStackTrace();
}

//cancel remaining tasks
future2.cancel(true);
future3.cancel(true);

service.shutdown();

//print whether the tasks were cancelled or completed
System.out.println("First task cancelled " +
        future1.isCancelled());
System.out.println("Second task cancelled " +
        future2.isCancelled());
System.out.println("Third task cancelled " +
        future3.isCancelled());

Listing 2. Code to illustrate cancellation of Runnable tasks

Creating Runnable task with a timeout

The get(long timeout, TimeUnit unit) method of Future waits for the timeout period for the execution of the task to complete. This method returns the result if the execution is completed before the timeout period or the timeout period is elapsed before the completion of execution or the thread is interrupted while waiting. It throws TimeoutException if the timeout was elapsed before completion of execution or InterruptedException if the thread was interrupted or CancellationException if the execution was cancelled or ExecutionException if the execution threw an exception. For Runnable task, this method returns null.

Listing 3. shows a Runnable task which prints numbers. The boolean property stop is checked before printing the number. Listing 4 shows the code which uses ExecutorService to create a Future. The timeout is specified on the task by calling the timed get method of Future. When the timeout period is elapsed before the completion of task, the TimeoutException handler sets the stop property of the PrintNumber task to false to stop the task.

public class PrintNumber implements Runnable {
    private boolean stop;

    public void setStop(boolean stop) {
        this.stop = stop;
    }

    public void run() {
        for(int i=1; !stop && i <= 500; i++) {
            System.out.println(i);
        }
    }
}

Listing 3. Runnable task to print numbers

 

PrintNumber task = new PrintNumber();
ExecutorService service = Executors.newSingleThreadExecutor();
Future<?> future1 = service.submit(task);

//wait for 100 microseconds for the task to run
try {
    future1.get(100,TimeUnit.MICROSECONDS);
}
catch (InterruptedException e) {
    e.printStackTrace();
}
catch (ExecutionException e) {
    e.printStackTrace();
}
catch (TimeoutException e) {
    task.setStop(true);
    System.out.println("Task has timed out");
}
service.shutdown();

Listing 4. Code to illustrate use of ExecutorService to create a Runnable task with timeout