JDK 1.5 concurrent utilities has provided many classes which make our life much easier while building complex multithreading applications.
The higher-level utilities in java.util.concurrent fall into three categories:
- the Executor Framework,
- concurrent collections,
- synchronizers.
In this article we will discuss all of these three utilities in detail.
1. Executor Framework:
Creating a work queue now requires only a single line of code:
ExecutorService executor = Executors.newSingleThreadExecutor();
This executor object takes a runnable or callable type to execute:
executor.execute(runnable);
Following code tells the executor to terminate gracefully:
executor.shutdown();
If you want more than one thread to process requests from the queue, simply call a different static factory that creates a different kind of executor service called a thread pool. You can create a thread pool with a fixed or variable number of threads. The java.util.concurrent.Executors class contains static factories that provide most of the executors you’ll ever need. If, however, you want some thing out of the ordinary, you can use the ThreadPoolExecutor class directly. This class lets you control nearly every aspect of a thread pool’s operation.
There is a CachedThreadPool available in the executor framework that is immensely helpful for not so heavy applications.
In a cached thread pool, submitted tasks are not queued but immediately handed off to a thread for execution. If no threads are available, a new one is created. If a server is so heavily loaded that all of its CPUs are fully utilized, and more tasks arrive, more threads will be created, which will only make matters worse. Therefore, in a heavily loaded production server, you are much better off using Executors.newFixedThreadPool, which gives you a pool with a fixed number of threads, or using the ThreadPoolExecutor class directly, for maximum control.
The key abstraction is no longer Thread, which served as both the unit of work and the mechanism for executing it. Now the unit of work and mechanism are separate. The key abstraction is the unit of work, which is called a task. There are two kinds of tasks: Runnable and its close cousin, Callable (which is like Runnable, except that it returns a value). The general mechanism for executing tasks is the executor service.
2. Concurrent Collections
ConcurrentHashMap
BlockingQueue extends Queue and adds several methods, including take, which removes and returns the head element from the queue, waiting if the queue is empty. This allows blocking queues to be used for work queues (also known as producer-consumer queues), to which one or more producer threads enqueue work items and from which one or more consumer threads dequeue and process items as they become available.
3. Synchronizers
Synchronizers are objects that enable threads to wait for one another, allowing them to coordinate their activities. The most commonly used synchronizers are CountDownLatch and Semaphore. Less commonly used are CyclicBarrier and Exchanger.
Countdown latches are single-use barriers that allow one or more threads to wait for one or more other threads to do something. The sole constructor for CountDownLatch takes an int that is the number of times the countDown method must be invoked on the latch before all waiting threads are allowed to proceed.