Monday, February 16, 2015

How to use ExecutorService to create and maintain a thread pool in java

The ExecutorService interface and its implementations which are found in java.util.concurrent package are very useful when you need to maintain a thread pool in your application.

What is a thread pool and why it is used? 
Well, first of all think that a thread pool as a collection of threads. Assume that you have a runnable class and assume that you need 'n' number of threads of this class run in parallel. If one thread finished its task and exit you may need to start another thread to make sure that 'n' number of threads are running in parallel.
The ExecutorService can do this and more other stuff for you.
You can add any number of threads in to the ExecutorService. You can tell the ExecutorService how many threads that you want to run in parallel. The ExecutorService is responsible of keeping the given number of threads running in parallel. If one thread went down by finishing its task, the ExecutorService starts another thread to guarantee that given number of threads are running in parallel. ExecutorService does this until all the threads in its pool are executed or until we signal it to stop.
Below example will show you how to create a thread pool using ExecutorService.

As I mentioned earlier you need to have a runnable class which acts as a thread. My runnable class is as below.

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyThread implements Runnable {
  @Override
  public void run() {
    System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date()));
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      System.out.println("Interrupted.......");
    }
  }
}

This is my code which creates and executes the thread pool.

 java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PoolTest {
  public static void main(String[] args) throws InterruptedException {
    int poolSize = 5;
    ExecutorService threadPool = Executors.newFixedThreadPool(poolSize);
    for (int i = 0; i < 20; i++) {
      MyThread thread = new MyThread();
      threadPool.execute(thread);
    }
    System.out.println("All threads are added to the pool....");
    threadPool.shutdown();
    System.out.println("All Executed....");
  }
}

Run and inspect the output of the above method and you will see that five threads have been executed at the same time.
I'll explain the above code line by line.
ExecutorService threadPool = Executors.newFixedThreadPool(poolSize);
This line creates the thread pool. The integer value passed to this method is the number of threads run in parallel.

threadPool.execute(thread);
The execute() method is used to add threads in to the thread pool. As soon as the execute() method is called first time, the ExecutorService starts execution of threads.

threadPool.shutdown();
After adding all the elements in to the thread pool, we calls this method to tell the ExecutorService, that we are not goint to add more threads in to the pool and it may gracefully shuts down after all the threads provided are executed. It should be noted that, this line does not shuts the thread pool down immediately. Instead it permits the thread pool to shuts down after it completely executed all the threads provided. Another thing to keep in mind is that you can't add more threads in to this thread pool after the shutdown() method is called. It will throw a runtime exception.

If you want to tell the ExecutorService to abort all the tasks and shuts down immediately, you can call threadPool.shutdownNow().

If you inspect the output of the above code carefully, you will see that the line "All Executed...." has been printed before the execution of all threads are finished. What is the reason for that. Yes, as you have already understood the thread pool does its job in background and the main thread which contains the line System.out.println("All Executed...."); gets executed in parallel.

What we can do if we need to print this line after all the threads were executed?
The easiest way is to call awaitTermination() after the shutdown() method. This method blocks the main thread until all tasks have completed execution after a shutdown request(or the given timeout occurs, or the current thread is interrupted, whichever happens first).
After adding the awaitTermination() method, the last lines of the above code will be as below.

System.out.println("All threads are added to the pool....");
threadPool.shutdown();
threadPool.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("All Executed....");

Now run the code and you will see that the line "All Executed...." is printed after all the threads are executed.

The other way is to use the ExecutorService.isTerminated() together with Thread.sleep() to check and wait until ExecutorService has been shut down. For an example you can replace the line threadPool.awaitTermination(1, TimeUnit.MINUTES); with below lines.

while (!threadPool.isTerminated()) {
  try {
Thread.sleep(500);
  } catch (InterruptedException e) {
e.printStackTrace();
  }
}

Working with multiple threads is some what tricky and you should act carefully. This tutorial demonstrated the basic usage of ExecutorService to handle thread pools. I think that would help you to design your next multi threaded program in more efficient way.

No comments:

Post a Comment