Architecture & DesignAn Introduction to Concurrent Collection APIs in Java

An Introduction to Concurrent Collection APIs in Java

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The concurrent collection APIs, apart from the Java Collection API, are a set of collections APIs that are designed and optimized specifically for synchronized multithreaded access. They are grouped under the java.util.concurrent package. This article provides an overview and introduces its use by using an appropriate example scenario.

An Overview

Java has supported multithreading and concurrency from its inception. The threads are created by either implementing the Runnable interface or extending the Thread class. The synchronization is achieved by the keyword called synchronization. Java also provides the mechanism for communication between the threads. This is achieved with the help of the notify() and wait() methods, which are part of the Object class. Although these innovative multithreading techniques are part of some of the excellent features of Java, they fall somewhat short in providing the need of a programmer who requires intensive multithreading capability out of the box. This is because a concurrent program needs more than just being able to create threads and do some rudimentary manipulations. It requires many high-level features such as thread pools, execution managers, semaphores, and so forth.

Existing Collection Framework

Java already has a full-blown collection framework. The collections are very good at what they do and can be used in Java thread applications, as well. Also, there is a keyword, called synchronized, to make them thread safe. Although superficially it may seem they are nice to be used in multithreading, the way the thread safety is achieved is the main bottleneck in its concurrent implementation. Apart from explicit synchronization, they are not designed under the paradigm of concurrent implementation from the beginning. The synchronizing of these collections is achieved by serializing all access to the collection’s state. This means that, although we may have some concurrency, due to underlying serialized processing it works on a principle which is actually the opposite. Serialization tolls heavy on the performance, especially when multiple threads compete for the collection-wide lock.

New Collection APIs

Concurrent collection APIs are an addition to Java from version 5 and are a part of the package called java.util.concurrent. They are an improvement of existing collection APIs and have been designed for concurrent access from multiple threads. For example, ConcurrentHashMap is actually the class we need when we want to use a synchronized hash-based Map implementation. Similarly, if we want a traversal-dominant, thread-safe List, we actually can use the CopyOnWriterArrayList class. The new ConcurrentMap interface provides a number of compound actions under a single method, such as putIfPresent, computeIfPresent, replace, merge, and so on. There are many such classes that are within the new concurrent collection framework. To name a few: ArrayBlockingQueue, ConcurrentLinkedDeque, ConcurrentLinkedQueue, ConcurrentSkipListMap, ConcurrentSkipListSet, CopyOnWriteArraySet, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedTransferQueue, PriorityBlockingQueue, SynchronousQueue, and others.

Queues

The collection types, such as Queue and BlockingQueue, may be used to hold an element temporarily, awaiting is retrieval in a FIFO manner for processing. ConcurrentLinkQueue, on the other hand, is a traditional FIFO queue implemented as an unbounded, thread-safe queue based on linked nodes. PriorityBlockingQueue is an unbounded blocking queue which uses same ordering norms as that of non-concurrent PriorityQueue and supplies blocking retrieval operations.

Maps

In older collection classes when synchronization is applied, it holds locks for the duration of each operation. There are operations, such as the get method of HashMap or contains method of List, which involve intricate computation behind the scene when invoked. For example, to find a specific element in a list, it automatically invokes the equals method. This method requires certain computation to compare each element on the list; it may take a long time to complete the task. This is worse in a hash-based collection. If the elements in the hash maps are unevenly distributed, traversing a long list and calling equals can take a very long time. This is a problem because it may affect the application’s overall performance.

Unlike HashMap, ConcurrentHashMap uses a different strategy altogether. Instead of providing a common lock for every synchronized method, it uses a technique called lock stripping. This is a better solution for both concurrency and scalability. Lock stripping uses separate locks for separate buckets. As a result, thread contention is decoupled from the underlying data structure and instead imposed on the bucket. For example, the implementation of ConcurrentHashMap uses an array of 16 locks—each of which guards 1/16 of the hash buckets; bucket N is guarded by lock N mod 16… this reduces the demand for any given lock by approximately a factor of 16. It is due to this technique that ConcurrentHashMap supports at least 16 concurrent writers by default and more can be accommodated on demand.

CopyOnWriterArrayList

It is a fine alternative to the synchronized List and does not require you to apply a locking mechanism during iteration. The iterators retain a reference to the backing array at the start of the iteration and do not change it. Therefore, it requires a brief synchronization to get the contents of the array. Multiple threads can access the collection without interfering with one another. Even modification from multiple threads do not suffer contention. There is a set counterpart of this array list, called CopyOnWriterSet, which can be used to replace synchronized Set on concurrency need.

A Quick Example

There are many classes in the concurrent collection. Their use is not that difficult for anyone familiar with the older collection framework. For the sake of completeness, here is an example to provide a glimpse into its uses in Java programming.

package org.mano.example;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ProducerConsumerDemo {
   static BlockingQueue<Integer> queue = new
      LinkedBlockingQueue<>(5);
   public static void main(String[] args) throws
         InterruptedException {
      int noOfProducers = 7;
      int noOfConsumers = 9;
      for (inti = 0; i < noOfProducers; i++) {
         new Thread(new Producer(), "PRODUCER").start();
      }
      for (int i = 0; i < noOfConsumers; i++) {
         new Thread(new Consumer(), "CONSUMER").start();
      }
      System.exit(0);
   }
   static class Producer implements Runnable {
      Random random = new Random();
      public void run() {
         try {
            int num = random.nextInt(100);
            queue.put(num);
            System.out.println("Produced: " + num
               + " Queue size : "+ queue.size());
            Thread.sleep(100);
         } catch (InterruptedException ex) {
            System.out.println("Producer is interrupted.");
         }
      }
   }
   static class Consumer implements Runnable {
      public void run() {
         try {
            System.out.println("Consumed: " + queue.take()
               + " Queue size : "+ queue.size());
            Thread.sleep(100);
         } catch (InterruptedException ex) {
            System.out.println("Consumer is interrupted.");
         }
      }
   }
}

Conclusion

Perhaps the greatest benefit of using the concurrent collection classes is their scalability and low risk. The concurrent collection APIs of Java provide a range of classes that are specifically designed to deal with concurrent operations. These classes are alternatives to the Java Collection Framework and provide similar functionality except with the additional support of concurrency. Therefore, the learning curve for the programmer who already know about the Java Collection Framework is almost flat. The classes are defined in the package java.util.concurrent. Here, I have tried to give an overview to get started and use the collection APIs wherever necessary.

References

  • Java API Documentation
  • Goetz, Brian, and Tim Peierls. Java Concurrency in Practice. Pearson, 2013.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories