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.