Multithreading is one of the important features of Java programming language. A thread runs a task and thus multiple threads enable execution of multiple tasks concurrently in a Java application. Prior to Java 5, Thread
class was instantiated explicitly to create a thread. The thread pool implementation also was not provided by the API.
With the Concurrency API introduced in Java 5, there is no more need to explicitly instantiate Thread
class to create a thread. The Executor framework included in the Concurrency API provides many facilities for multithreaded programming such as creating threads, creating various types of thread pools, submitting tasks to a thread pool, performing asynchronous execution of tasks etc.
It is recommended to use the Executor framework instead of using the Thread class directly in the code. The Executor framework allows decoupling of the code to create thread pool and threads and the code for tasks which the threads should execute. The former part is now taken care by the Concurrency API.
In one of my previous blog posts on Active Object Pattern, I had written about how we had used it to read the messages from the message queue (MQ) and parse them. We had used Executors
to create a thread pool. A thread from the pool parsed a message and thus messages were parsed concurrently. The use of Executor framework saved the effort of writing code to create threads and managing a thread pool.
I’ll be writing about the Executor framework in a series of blog posts. In this blog post I have explained the following uses of the framework:
The Concurrency API provides Executor
, ExecutorService
and ScheduledExecutorService
interfaces. An instance of class implementing Executor
is used to submit a Runnable
task for execution instead of explicitly creating threads with Thread
class. The ExecutorService
is a subinterface of Executor
.
ExecutorService
allows submission of Callable
tasks for asynchronous execution and creating cancellable Runnable tasks. Callable
is an interface like Runnable
and it is part of Concurrency API. The factory class - Executors
of Concurrency API is generally used to obtain the implementation of ExecutorService
and submit the Runnable
or Callable
tasks for execution.
Executing tasks sequentially with a single thread
The Executors.newSingleThreadExecutor()
method returns an ExecutorService
which uses a single worker thread to execute the Runnable
or Callable
tasks. An unbounded queue is used to held the tasks submitted to it which means that there is no upper limit on the number of tasks submitted for execution. The tasks are executed sequentially in the order of submission. Since only one thread is running them only one task will be running at a time. One advantage of using this factory method is that if the thread terminates due to an exception during execution of a task before shutdown of the ExecutorService
, a new thread will be created and it will resume execution of the subsequent tasks.
The use of Executors.newSingleThreadExecutor()
is given below in Listing 2. To illustrate the use of this method, I have used a simple Runnable task given in Listing 1. which prints Hello.
public class PrintHello implements Runnable { public void run() { System.out.println(“Hello”); } }
Listing 1. A simple Runnable task to print Hello
ExecutorService service = Executors.newSingleThreadExecutor(); PrintHello task = new PrintHello(); //print Hello twice service.execute(task); service.execute(task); service.shutdown();
Listing 2. Using the Executors.newSingleThreadExecutor()
to run the task
Shutting down ExecutorService
The shutdown()
method of ExecutorService
should be explicitly called to stop it. It shuts down the ExecutorService
after all the submitted tasks are executed. This method does not throw any exception if the ExecutorService
has already stopped. The isShutdown()
method of ExecutorService
can be called to check whether it is stopped.
The shutdownNow()
method of ExecutorService
attempts to shutdown by stopping all active tasks in execution and cancelling all the waiting tasks. The list of Runnable
tasks that were waiting for execution is returned by this method. This method does not guarantee stopping of active tasks. The active tasks are stopped by thread interrupt. If a thread is not interrupted the task may not stop.
Creating a thread pool with fixed number of threads
The Executors.newFixedSizeThreadPool(int n)
creates a thread pool of n number of threads and returns an ExecutorService
. This thread pool uses an unbounded queue which means that any number of tasks can be submitted to it for execution. This method may create all the threads at once or may create them as they are required. At max n number of threads will be active at any point during its execution. If more number of tasks are submitted than the number of active threads, then the remaining tasks will be held in the queue. As soon as a thread completes execution of a task, it can take over the execution of the waiting task from the queue.
For performance tuning, the number of threads to be created by the thread pool is usually defined in a properties file.
Listing 3 given below illustrates the creation of a thread pool and submitting tasks to it for execution.
//Read this value from a properties file int thread_count = 2; ExecutorService service = Executors.newFixedSizeThreadPool(thread_count); PrintHello task = new PrintHello(); //print Hello 10 times for(int i=1; i <=10; i++) service.execute(task); } service.shutdown();
Listing 3. Creating a thread pool of multiple threads and submitting the task for execution