Thursday, 16 June 2011

The Executor service tutorial

An asynchronous process is a process which has the probability to not finishing its execution right away, but later. If the execution finishes right away, then those are known as synchronous process. Special care is needed when designing asynchronous process, because we are used to synchronous programming model.

The java.util.concurrent.ExecutorService interface represents an asynchronous execution mechanism which is capable of executing tasks in the background. It extends Executor interface.

Implementation of Executor service
Here are the type of thread pools you can create with the Executors class, which are got from the factory class called Executors. Following are the implementation of ExecutorService :
Single Thread Executor : A thread pool with only one thread. So all the submitted task will be executed sequentially.
Method : Executors.newSingleThreadExecutor()
Cached Thread Pool : A thread pool that create as many threads it needs to execute the task in parralel. The old available threads will be reused for the new tasks. If a thread is not used during 60 seconds, it will be terminated and removed from the pool.
Method : Executors.newCachedThreadPool()
Fixed Thread Pool : A thread pool with a fixed number of threads. If a thread is not available for the task, the task is put in queue waiting for an other task to ends.
Method : Executors.newFixedThreadPool()
Scheduled Thread Pool : A thread pool made to schedule future task.
Method : Executors.newScheduledThreadPool()
Single Thread Scheduled Pool : A thread pool with only one thread to schedule future task.
Method : Executors.newSingleThreadScheduledExecutor()

So we can create our ExecutorService like this:
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newFixedThreadPool(4);
ExecutorService executorService3 = Executors.newScheduledThreadPool(4);

Methods in Executor Service
There are a few different ways to delegate tasks for execution to an ExecutorService:
  • execute(Runnable) - The execute(Runnable) method takes a java.lang.Runnable object, and executes it asynchronously.There is no way of obtaining the result of the executed Runnable.
  • submit(Runnable) - The submit(Runnable) method also takes a Runnable implementation, but returns a Future object. This Future object can be used to check if the Runnable as finished executing.
  • submit(Callable)-The submit(Callable) method is similar to the submit(Runnable) method except for the type of parameter it takes. The Callable instance is very similar to a Runnable except that its call() method can return a result. The Runnable.run() method cannot return a result.
  • invokeAny(...) - The invokeAny() method takes a collection of Callable objects, or subinterfaces of Callable. Invoking this method does not return a Future, but returns the result of one of the Callable objects. You have no guarantee about which of the Callable's results you get. Just one of the ones that finish.
  • invokeAll(...) - The invokeAny() method takes a collection of Callable objects, or subinterfaces of Callable. Invoking this method does not return a Future, but returns the result of one of the Callable objects. You have no guarantee about which of the Callable's results you get. Just one of the ones that finish.

Lets look at them one by one. First get the instance of executor service:
ExecutorService executorService = Executors.newSingleThreadExecutor();

execute()
Example to be added soon.
submit()
submit() takes callable and returns Future object.
Callable c = new Callable() {
public Object call() throws Exception {
Thread.sleep(3000);
ReturnObject rt = new ReturnObject();
rt.setReturnName(“serkan sunel”);
return rt;
}
};

Future future = executorService.submit(c);
//Now get the result 
try {
Object returnObj = future.get();
System.out.println(“returned :+ returnObj);
} catch (Exception e) {
e.printStackTrace();
}

So we can get result of the thread.

Closing the ExecutorService
You can close the executor service by calling shutdown() method. But this will not shut down immediately, but it will no longer accept new tasks, and once all threads have finished current tasks, the ExecutorService shuts down. All tasks submitted to the ExecutorService before shutdown() is called, are executed. You can use shutdownNow() to shutdown the service immediately.

Example
We are going to look into a program, which will initialize a fixed pool size ExecutorService and submit number of tasks (threads) to the executor service to execute them. To simulate asynchronous behavior each of those threads will work, and the sleep for a while, and then again work before returning result.

public class ExecutorServiceWithCallable {

public void executorServiceForCallable(int poolSize) throws
ExecutionException, InterruptedException {

ExecutorService executorService = Executors.newFixedThreadPool(poolSize);

List<Future> futures = new ArrayList<Future>();
for (int index = 0; index < poolSize; index++) {
Future future = executorService.submit(new RepeatingGreetings(index));
futures.add(future);
}

for (Future future : futures) {
System.out.println(future.get());
}
}
class RepeatingGreetings implements Callable {
private int index;

RepeatingGreetings(int index) {
this.index = index;
}

@Override
public V call() throws Exception {
System.out.println("Callable: " + index + " is working");
int sleepValue = (int) (Math.random() * 1000);
Thread.sleep(sleepValue);
return (V) ("Callable: " + index + " sleept for: " + sleepValue);
}
}
}

First we are going to talk about the Callable we are creating, and then we will look into ExecutorService.

Callable is same as java.lang.Runnable, both of them are designed to be executed by another thread. The only difference is, Callable is designed to return result, but Runnable are not. The call() method of Callable works same as run() method of Runnable.

The use of Callable on our example is pretty straight forward. The RepeatingGreetings is Callable which will print a message, sleep for a random amount of time, and then return a string message as result of execution.

The ExecutorServiceWithCallable is doing the main job here. We are creating a new executor service for supplied pool size. Then we are submitting our Callables to the ExecutorService to execute. As soon as we invoke the submit() method of ExecutorService the Callable are handed over to ExecutorService to execute. Here one thing we have to note, the submit() is not blocking. So, all of our Callables will be submitted right away to the ExecutorService, and ExecutorService will decide when to execute which callable. For each Callable we get a Future object to get the result later.

Once the Callables are submitted for execution, we are iterating through all Futures to get the result of execution. We are invoking the get() method of Future to get the result. Here we have to remember that, the get() is a blocking method. So the first invocation of get() will wait until that Callable is finished. Once we receive the result of the execution of first Callable it will wait for next Callable to finish, and so on. Since the get() is blocking, we could put the iteration on a separate thread to continue the execution, and get the result later when all Futures are ready with their results.

There is also, another useful method named isDone() on Future which let us check if the Callable for that Future is finished or not.

Output
The two random execution of our above example for five Callables produces output like

Callable: 0 is working
Callable: 1 is working
Callable: 3 is working
Callable: 2 is working
Callable: 4 is working
Callable: 4 sleept for: 18
Callable: 0 sleept for: 70
Callable: 1 sleept for: 449
Callable: 3 sleept for: 744
Callable: 2 sleept for: 844
Another Random output -
Callable: 0 is working
Callable: 1 is working
Callable: 3 is working
Callable: 2 is working
Callable: 4 is working
Callable: 0 sleept for: 270
Callable: 1 sleept for: 348
Callable: 2 sleept for: 427
Callable: 4 sleept for: 428
Callable: 3 sleept for: 564

See also - Implementing Thread Pools via ExecutorService 

No comments:

Post a Comment