Java Concurrency utilities



Concurrency Utilities Java Multithreading

The java.util.concurrent package contains various Java concurrency utilities that provide various classes for multithreading. This package is present from Java 5 onwards. Prior to this, the user has to create their own utilities for developing concurrent applications.

In this tutorial, we will see an overview of all the Java concurrency utilities.

Java Concurrency Utilities

The java.util.concurrent package contains the below classes that we can use to implement concurrency or multithreading.

java.utitl.concurrent package

ExecutorService

The ExecutorService is an interface that is part of the java.util.concurrent package that performs asynchronous task execution. This Java concurrency utility performs the task execution concurrently in the background. While creating an ExecutorService, we can create a thread pool with the required size by using the method newFixedThreadPool(int size). We can also create a single thread by using the method newSingleThreadExecutor(ThreadFactory threadFactory). Another way is to use the newScheduledThreadPool(int size).

ExecutorService exec = Executors.newFixedThreadPool(5);
ExecutorService exec = Executors.newSingleThreadExecutor();
ExecutorService exec = Executors.newScheduledThreadPool(5);

There are multiple ways in which we can delegate a task to the ExecutorService for execution.

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny()
  • invokeAll()

Once the execution is complete, it is very important to shutdown the ExecutorService using any of the below methods.

  • shutdown()
  • shutdownNow()
  • awaitTermination()

ScheduledExecutorService

This is also an ExecutorService that executes the task after a scheduled time interval or after a particular delay. We can specify the number of threads we want to create using the newScheduledThreadPool. When calling the schedule method, we can specify the time interval after which the task has to be executed.

ScheduledExecutorService sch = Executors.newScheduledThreadPool(5);
ScheduledFuture sf = sch.schedule(new Callable() {
        -----code----
}, 5, TimeUnit.SECONDS);

After we create an instance of the ScheduledExecutorService, we can perform the task execution of this concurrency utilitiy using any of the below methods.

  • schedule(Callable task, long delay, TimeUnit timeunit)
  • schedule(Runnable task, long delay, TimeUnit timeunit)
  • scheduleAtFixedRate(Runnable, long intialDelay, long period, TimeUnit timeunit)
  • scheduleWithFixedDelay(Runnable, long initialDelay, long period, TimeUnit timeunit)

Similar to the ExecutorService, we need to shut down the ScheduledExecutorService either by using the shutdown() method or shutdownNow() method.

BlockingQueue

BlockingQueue is an interface that is part of the java.util.concurrent package. It allows multiple threads to insert and remove elements from the queue concurrently. It also has the capability to block the threads during the insertion and deletion of elements. This means it can block a thread during deletion operation when there are no more elements in the queue and waits until there is at least 1 element. Similarly, it blocks during insertion operation when we try to insert an element when the queue has reached its maximum capacity. In a BlockingQueue, one thread always inserts an element while the other thread removes the element from the queue.

We can use the add() or offer() methods to insert elements and use the remove(), take(), or poll() method to remove an element from the BlockingQueue. To retrieve the first element we can use the peek() or element() method.

ArrayBlockingQueue

ArrayBlockingQueue is a class that implements the BlockingQueue interface. It internally stores the elements in the form of an array. We can specify the upper bound during the instantiation of the object and cannot change this later.

BlockingQueue bq = new ArrayBlockingQueue(1024);

DelayQueue

The DelayQueue class implements the BlockingQueue interface. This class blocks the elements until the specified time delay has expired. To use the DelayQueue class, the elements must implement the java.util.concurrent.Delayed interface.

LinkedBlockingQueue

The LinkedBlockingQueue class also implements the BlockingQueue interface. It internally stores the elements in the form of a LinkedList structure and maintains FIFO(FirstInFirstOut). This means the head elements represent the element that we insert first and the tail element represents the last element that we insert.

BlockingQueue bq = new LinkedBlockingQueue();

PriorityBlockingQueue

The PriorityBlockingQueue class implements the BlockingQueue interface. The functionality is the same as java.util.PriorityQueue. It is mandatory for all the elements to implement the Comparable interface. Based on the implementation in the Comparable interface, it orders the elements accordingly.

BlockingQueue bq = new PriorityBlockingQueue();

SynchronousQueue

The SynchronousQueue class implements the BlockingQueue interface. It allows exchanging only a single element at a time. This means the thread has to wait to insert an element until another thread removes an element from the queue. It maintains synchronization in such a way that when a thread tries to remove an element when the queue is empty, it blocks until it inserts an element into the queue.

BlockingDeque

The BlockingDeque is an interface that is part of the java.util.concurrent package. It represents a deque and blocks the elements from inserting or removing if such operations are not possible. Deque stands for Double Ended Queue. This means we can insert and delete elements from both ends.

LinkedBlockingDeque

The LinkedBlockingDeque class implements the BlockingDeque interface. It represents a double-ended queue that supports insertion and deletion operations from both ends.

BlockingDeque dq = new LinkedBlockingDeque();

Callable and Future

Java Callable and Future interfaces is a common concept in multithreading concept. We can use this mainly when we want the threads to return some values. This is part of the concurrency package java.util.concurrent and is available from Java 5 onwards. We can use the Future object to find the status of the Callable tasks. The Callable tasks return a future object.

CountDownLatch

The CountDownLatch is part of the java.util.concurrent package that blocks a group of threads until another operation completes. We need to initialize the CountDownLatch with a counter value that decrements each time we call the countDown() method. When this counter value reaches 0, other threads also get released.

CountDownLatch cd = new CountDownLatch(int counter);

CyclicBarrier

CyclicBarrier implements synchronization wherein it synchronizes the threads or acts as a barrier. This means all the threads need to wait or go through this barrier before it continues its process. For this, it uses the await() method on the CyclicBarrier. After all the threads reach this barrier, it can resume and continue the execution.

CyclicBarrier cb = new CyclicBarrier(int count);

cb.await();

ConcurrentMap

The ConcurrentMap is an interface that is part of the java.util.concurrent package where it represents a Map structure. It has its own methods apart from the methods present in the Map interface. Since it is an interface, we need to implement the functionality and hence can use the ConcurrentHashMap.

ConcurrentMap cm = new ConcurrentHashMap();
cm.put("key","value");

ConcurrentNavigableMap

The ConcurrentNavigableMap is an interface that is also part of the java.util.concurrent package where it represents a NaviagableMap structure. It supports concurrent access for the subMaps as well that we get using the headMap(), tailMap().

ConcurrentNavigableMap cn = new ConcurrentSkipListMap();

Exchanger

Exchanger acts as a container where two threads can exchange objects with each other. It is present in the java.util.concurrent package. The threads can exchange the objects by using the exchange() method.

Exchanger ex = new Exchanger();

ThreadPoolExecutor

The ThreadPoolExecutor provides an implementation for the ExecutorService. It executes the tasks of Callable or Runnable using pooled threads. A thread pool can contain N number of threads and we can determine its size using the corePoolSize or maximumPoolSize variables.

ForkJoinPool

The ForkJoinPool present in the java.util.concurrent package helps to split the tasks and allows the subtasks to execute concurrently. Once all the subtasks complete the execution, it merges and joins again. This process contains two steps: fork(split the task) and join(merge the subtask). The ForkJoinPool is available from Java 7 onwards.

ForkJoinPool fp = new ForkJoinPool(int count);

Java Concurrency Utilities

Locks

A lock ensures that only the currently executing thread has access to the shared resource and blocks other threads from accessing it until the current thread releases the resource. This is mainly used for synchronization and we can use its methods like a lock() and unlock() in separate methods. It is used to prevent race conditions and critical sections. To create a Lock, we need to create a class that defines the Lock interface. The lock() method locks the object and the unlock() method unlocks the object. It is safe to use the critical sections always after locking the resource.

ReentrantLocks

A reentrant lock is an implementation of the Lock interface. We can use reentrant locks when we want the thread to lock the same resource multiple times. While using the reentrant locks, it internally increments the count value when we call the lock() method and decrements the value when we call the unlock() method.

Lock l = new ReentrantLock();

try {
    l.lock();
    //critical section
}
finally{
    l.unlock();
}

Reentrant locks are useful while implementing concurrency. The Reentrant lock has its own methods like isLocked(), getHoldCount(), etc which we will see in detail in separate tutorials.

ReadWriteLocks

ReadWriteLocks allows multiple threads to use the lock for reading but allows only one lock for writing. There are certain rules for implementing ReadLock and WriteLock.

ReadLock: Multiple threads can obtain a ReadLock when no other threads are using a WriteLock or requested a WriteLock.

WriteLock: Only one thread can obtain the WriteLock at a time when there are no other threads reading or writing.

Semaphore

Semaphore is one of the Java concurrency utilities that is used to block the thread access to critical sections. Before the thread enters the critical section, it checks whether the semaphore has a permit or not. If there is a permit, it allows the thread to enter else it will not. It contains a method to check the number of available permits. Semaphores can also be used for thread signaling.

  • Counting semaphore: This type of semaphore can count the number of signals sent using the take() method.
  • Bounded semaphore: It is a semaphore that has an upper bound of the number of signals it can store

Semaphore has 2 main methods: acquire() and release(). Whenever we call the acquire() method, it takes a permit into account, and whenever we invoke the release() method, it returns back the permit to the counting semaphore.

ThreadFactory

A ThreadFactory helps to create a thread pool whenever required. We can create a new thread in a thread pool by using the newThread(Runnable r) method at runtime.

Phaser

Phaser is also a flexible barrier that blocks multiple threads for execution. It executes a number of threads dynamically in separate phases. It is a better flexible approach when compared to the CyclicBarrier and CountDownLatch.

Atomic

The atomic interface is part of the java.util.concurrent package that provides various classes to support atomic operations like compareAndSet(). It has various classes as listed below:

  • AtomicInteger: Provides integer variable to read and write atomically
  • AtomicLong: Provides Long variable to read and write atomically
  • AtomicBoolean: Provides a boolean variable to read and write atomically
  • AtomicReference: Provides an object reference variable to read and write atomically
  • AtomicStampedReference: Provides an object reference variable to read and write atomically
  • AtomicIntegerArray: Represents an array of integer values to perform atomic operations
  • AtomicLongArray: Represents an array of long values for atomicity
  • AtomicReferenceArray: Represents an array of reference variables

Reference