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:
- Creating asynchronous task
- Creating cancellable Runnable task
- 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