ConcurrentHashMap in Java

ConcurrentHashMap is a thread-safe variant of HashMap that allows concurrent access and modification.

Key Characteristics

·        Thread Safety: Multiple threads can operate on the map concurrently without the need for explicit synchronization.

·        Performance: It provides better performance compared to Collections.synchronizedMap due to finer-grained locking.

·        Atomic Operations: Supports atomic operations like putIfAbsent, remove, replace, etc.

Segmentation: ConcurrentHashMap divides the hash table into multiple segments. By default, there are 16 segments, but this can be customized using the concurrencyLevel parameter in the constructor. Each segment has its own lock, allowing multiple threads to operate on different segments concurrently. This reduces contention and improves performance.

Separate Chaining with Linked Lists: Similar to HashMap, ConcurrentHashMap uses separate chaining with linked lists to handle collisions. When a collision occurs, the colliding keys are stored in a linked list within the same bin. However, ConcurrentHashMap uses a specialized variant of a linked list called a "HashEntry" list, which is designed for better concurrency.

Synchronized Access within Segments: When two threads attempt to access the same segment, one thread acquires the lock for that segment while the other thread waits. This ensures that only one thread can modify the segment at a time, preventing conflicts.

Rehashing: If a segment exceeds its load factor (i.e., it becomes too full), it is rehashed to create a new segment with a larger capacity. This process is performed by a single thread and does not affect other threads accessing different segments.

Tree-Based Approach for Frequent Collisions: If a segment experiences frequent collisions, ConcurrentHashMap may switch to a tree-based structure (similar to a balanced tree) for that segment to improve lookup performance.

Atomic Operations: ConcurrentHashMap uses atomic operations, such as compare-and-swap (CAS), to ensure that updates are performed safely and atomically, even in the presence of multiple threads.

static class Node<K,V> implements Map.Entry<K,V> {

    final int hash;

    final K key;

    volatile V val;

    volatile Node<K,V> next;

    ...

}

 

 

https://raw.githubusercontent.com/vsaravanan/java22/master/src/main/java/com/saravanjs/java22/console/collection/ConcurrentHashMapExample.java

 

 

 

 

public class ConcurrentHashMapExample {
   
public static void main(String[] args) {

       
//ConcurrentHashMap
       
Map<String,String> myMap = new ConcurrentHashMap<String,String>();
       
myMap.put("1", "1");
       
myMap.put("2", "1");
       
myMap.put("3", "1");
       
myMap.put("4", "1");
       
myMap.put("5", "1");
       
myMap.put("6", "1");
       
System.out.println("ConcurrentHashMap before iterator: "+myMap);
       
Iterator<String> it = myMap.keySet().iterator();

       
while(it.hasNext()){
           
String key = it.next();
           
if(key.equals("3")) myMap.put(key+"new", "new3");
        }
       
System.out.println("ConcurrentHashMap after iterator: "+myMap);

       
//HashMap
       
myMap = new HashMap<String,String>();
       
myMap.put("1", "1");
       
myMap.put("2", "1");
       
myMap.put("3", "1");
       
myMap.put("4", "1");
       
myMap.put("5", "1");
       
myMap.put("6", "1");
       
System.out.println("HashMap before iterator: "+myMap);
       
Iterator<String> it1 = myMap.keySet().iterator();

       
while(it1.hasNext()){
           
String key = it1.next();
           
if(key.equals("3")) {
               
myMap.put(key+"new", "new3");
                
break; // UNCOMMENT AND TRY
           
}
        }
       
System.out.println("HashMap after iterator: "+myMap);
    }
}

 

 

ConcurrentHashMap before iterator: {1=1, 2=1, 3=1, 4=1, 5=1, 6=1}

ConcurrentHashMap after iterator: {1=1, 2=1, 3=1, 4=1, 5=1, 3new=new3, 6=1}

HashMap before iterator: {1=1, 2=1, 3=1, 4=1, 5=1, 6=1}

HashMap after iterator: {1=1, 2=1, 3=1, 4=1, 5=1, 3new=new3, 6=1}

 

 

while(it1.hasNext()){
   
String key = it1.next();
   
if(key.equals("3")) {
       
myMap.put(key+"new", "new3");
        
// break; // UNCOMMENT AND TRY
   
}
}

 

 

ConcurrentHashMap before iterator: {1=1, 2=1, 3=1, 4=1, 5=1, 6=1}

ConcurrentHashMap after iterator: {1=1, 2=1, 3=1, 4=1, 5=1, 3new=new3, 6=1}

HashMap before iterator: {1=1, 2=1, 3=1, 4=1, 5=1, 6=1}

Exception in thread "main" java.util.ConcurrentModificationException

        at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1605)

        at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1628)

        at com.govtech.viswa.collections.concurrenthashmap.ConcurrentHashMapExample.main(ConcurrentHashMapExample.java:46)

 

 

 

 


// https://raw.githubusercontent.com/vsaravanan/java22/master/src/main/java/console/collection/ConcurrentHashMapExample3.java

public class ConcurrentHashMapExample3 {

   
public static void main(String[] args) {
       
// Create a ConcurrentHashMap
       
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

       
// Create a fixed thread pool
       
ExecutorService executorService = Executors.newFixedThreadPool(3);

       
// Create and submit tasks to the executor service
       
for (int i = 0; i < 3; i++) {
           
executorService.submit(() -> {
               
for (int j = 0; j < 5; j++) {
                   
String key = "key" + j;
                   
map.compute(key, (k, v) -> v == null ? 1 : v + 1);
                   
System.out.println(Thread.currentThread().getName() + " updated " + key + " to " + map.get(key));
                }
            });
        }

       
// Shutdown the executor service
       
executorService.shutdown();

       
// Wait for all tasks to complete
       
while (!executorService.isTerminated()) {}

       
// Print the final map
       
System.out.println("Final map: " + map);
    }
}

 

pool-1-thread-3 updated key0 to 3

pool-1-thread-2 updated key0 to 2

pool-1-thread-1 updated key0 to 2

pool-1-thread-3 updated key1 to 1

pool-1-thread-3 updated key2 to 1

pool-1-thread-2 updated key1 to 2

pool-1-thread-1 updated key1 to 3

pool-1-thread-3 updated key3 to 1

pool-1-thread-2 updated key2 to 2

pool-1-thread-1 updated key2 to 3

pool-1-thread-3 updated key4 to 1

pool-1-thread-2 updated key3 to 2

pool-1-thread-1 updated key3 to 3

pool-1-thread-2 updated key4 to 2

pool-1-thread-1 updated key4 to 3

Final map: {key1=3, key2=3, key0=3, key3=3, key4=3}