ConcurrentHashMap in Java

ConcurrentHashMap in Java is a thread safe Map implementation which provides another alternative to be used in a multithreaded environment apart from HashTable or explicitly synchronizing HashMap. ConcurrentHashMap is part of the java.util.concurrent package.

How is ConcurrentHashMap better option

Other thread safe implementations like HashTable or explicit synchronization of HashMap synchronize all the methods on a single lock and all the methods are synchronized, even if methods are for retrieving elements. That makes these options very slow-

  1. Since whole collection is locked so only a single thread can access it at a given time.
  2. Since all the methods are synchronized so read operations are also slow.

ConcurrentHashMap in Java tries to address these issues-

  1. By not locking the collection for retrieval operations like get(). Concurrent read operations are permitted only the write operations are synchronized.
  2. Even for write operations whole collection is not locked but only the part of the table where the elements has to be put as per the calculated hashcode.

Features of ConcurrentHashMap

Some of the features of the ConcurrentHashMap which are discussed in this post are as follows-

  1. A brief introduction on how ConcurrentHashMap is internally implemented in Java.
  2. ConcurrentHashMap in Java is thread safe.
  3. ConcurrentHashMap does not allow null as key or value.
  4. Iterator returned by ConcurrentHashMap do not throw ConcurrentModificationException if the map is structurally modified at any time after the iterator is created.

Internal implementation of ConcurrentHashMap in Java

For storing its element internally ConcurrentHashMap in Java uses an array named table of type Node.

Here Node class represents the Key-value entry and defined as a static class with in the ConcurrentHahsMap implementation. Node class has fields for storing key and value and also next field for holding the reference to the next node.

Here note that in initial implementation of ConcurrentHashMap there was array Segment which was used and that provided concurrency level of 16 by default i.e. 16 threads could access 16 elements stored in different indexes of the array because each segment could be locked independently. But Java 8 onwards internal implementation of ConcurrentHashMap is modified and now array named table is used and it mainly uses Compare-And-Swap (CAS) operations for concurrency during write operations.

Each array index in the table can still be independently locked by synchronizing the first node of that particular bucket.

internal implementation- ConcurrentHashMap in Java

Java ConcurrentHashMap constructors

  • ConcurrentHashMap()– Creates a new, empty map with the default initial table size (16).
  • ConcurrentHashMap(int initialCapacity)– Creates a new, empty map with an initial table size accommodating the specified number of elements without the need to dynamically resize.
  • ConcurrentHashMap(int initialCapacity, float loadFactor)– Creates a new, empty map with an initial table size based on the given number of elements (initialCapacity) and initial table density (loadFactor).
  • ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)– Creates a new, empty map with an initial table size based on the given number of elements (initialCapacity), table density (loadFactor), and number of concurrently updating threads (concurrencyLevel).
  • ConcurrentHashMap(Map<? extends K,? extends V> m)– Creates a new map with the same mappings as the given map.

Java example creating a ConcurrentHashMap

In this example a ConcurrentHashMap is created and (key, value) pair added to it which are later displayed.

Output

Null not allowed in Java ConcurrentHashMap

ConcurrentHashMap does not allow insertion of null as either key or value. So both of the following statements result in NullPointerException.

ConcurrentHashMap in Java is thread safe

ConcurrentHashMap in Java is safe to use in multi-threaded environment. Let’s see an example where we’ll first try to insert 400 elements in a HashMap (which is not thread safe) using 4 threads with each thread inserting 100 elements. Expected size of the HahsMap is 400 after the execution.

Output

As you can see size is 394 in one run because of the thread interference.

Using ConcurrentHashMap eliminates such inconsistencies. You just need to change the following line in the code.

Now the size is always 400.

ConcurretHashMap returns a fail-safe iterator

Iterator returned by ConcurrentHashMap is fail-safe and does not throw ConcurrentModificationException if the map is structurally modified at any time after the iterator is created.
Example code

Output

In the code, while iterating the ConcurrentHashMap a new element is added to it that does not result in ConcurrentModificationException being thrown.

Atomic operations in ConcurrentHashMap

Even though ConcurrentHashMap is thread safe but still atomic operations may give inconsistent result in multi-threaded environment. For example a scenario as follows.

Here if an executing thread is preemepted by another thread after the execution of this line- Integer newVal = (oldVal== null) ? 1 : oldVal + 1; then the value put back in the ConcurrentHashMap may not be correct.
In such scenarios it is better to use atomic operations. Some of the atomic operations in the ConcurrentHashMap class are-

  • putIfAbsent(K key, V value)– If the specified key is not already associated with a value, associates it with the given value.
  • remove(Object key, Object value)– Removes the entry for a key only if currently mapped to a given value.
  • computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)– If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unless null.
  • computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)– If the value for the specified key is present, attempts to compute a new mapping given the key and its current mapped value.
  • compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)– Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping).
  • merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)– If the specified key is not already associated with a (non-null) value, associates it with the given value.

Using the atomic operation compute, scenario as listed above can be written as follows-

Advantages and disadvantages of ConcurrentHashMap

ConcurrentHashMap performs better if there are more reads than writes in a multi-threaded environment as the concurrent read operations are permitted. Since retrieval operations are non-blocking, so may overlap with update operations (including put and remove). Thus the concurrent retrievals may or may not reflect insertion or removal of some entries.

If there are more writes and updates in the ConcurrentHashMap and HashCode implementation is not proper then many elements may have the same hashcode. In that scenario most of the threads will need to access the same table index where the elements with the same hashcode are to be stored resulting in degraded performance.

Iterators in ConcurrentHashMap are designed to be used by only one thread at a time.

That’s all for the topic ConcurrentHashMap in Java. If something is missing or you have something to share about the topic please write a comment.


You may also like

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.