Pooling threads to execute short tasks

If you develop programs that execute many short-lived tasks, it’s wise to take advantage of a technique called thread pooling. Instead of creating a thread for each new task and discarding the thread when the task is done, you can create a pool of threads and give the pool each task to execute. If a thread in the pool is available, the task executes immediately. The thread returns to the pool when the task is done. Otherwise, the task waits for a thread to become available from the pool before executing.

J2SE 5.0 offers a new java.util.concurrent package, and in that package there are concurrency utilities that provide a pre-built thread pooling framework. The Executor interface in java.util.concurrent provides a single method, execute, that accepts a Runnable object as follows:

public interface Executor {public void execute(Runnable command);}

To use the thread pooling framework, you create an Executor instance, then you pass it some runnable tasks:

Then you create or find an implementation of the Executor interface. The implementation could run the task immediately, in a new thread, or serially. For example, here is an implementation that spawns a new thread for each task:

The concurrency utilities also include a ThreadPoolExecutor class that offers support for many common pooling operations. With one of the four ThreadPoolExecutor constructors, you can specify options such as pool size, keep alive time, a thread factory, and a handler for rejected threads:

But you really don’t need to call a constructor. Instead, the Executors class of the java.util.concurrent package creates the thread pool for you. In the simplest case, you call the newFixedThreadPool method in the Executors class and pass in the number of threads you want in the pool. You then use ExecutorService, an interface that extends Executor, to either execute Runnable tasks or submit them. Calling the submit method of ExecutorService allows you to get a result back. The submit method also returns a Future object that you can use to check if the task is done.

Let’s run a test program to demonstrate the use of thread pools. First, here’s a program, NamePrinter, that notifies you when it starts, pauses for some amount of time, and then notifies you when it’s done.

Here is the test program, UsePool. It creates a thread pool of size 3, and adds 10 tasks to it (that is, 10 runs of NamePrinter). The UsePool program then waits for the tasks to finish before calling shutdown and awaitTermination. An ExecutorService should be shutdown before being terminated. There is also a shutdownNow method which attempts an immediate shutdown. Termination here is even faster than through the shutdown method. The shutdownNow method returns a List of any remaining Runnable tasks.