Tuesday 29 December 2015

Implementing thread safety using ConcurrentHashMap


When your program can be accessed by multiple threads concurrently, you need to ensure that it is thread-safe. 
Stateless objects which do not share any state between the method calls are always thread-safe. However, when the state is shared across multiple methods of a class and multiple threads can call these methods concurrently then you need to take explicit measures to ensure thread safety.



In this blog, I am writing about implementing thread  safety  using  ConcurrentHashMap

ConcurrentHashMap uses this finer-grained locking mechanism which is called lock stripping with which it provides 16 locks by default and reduces lock contention when multiple threads access a ConcurrentHashMap object concurrently.

Lock Stripping
Since the underlying data is stored in a series of buckets in a hash map , it is more efficient to lock only a bucket  while accessing a map object instead of obtaining lock on the entire map object.the mechanism which lock only portion of the map object instead of entire map is called lock stripping.

Key Points :-

  •  The default concurrency Level of ConcurrentHashMap is 16 .You can choose any concurrency level but  higher number can be waste of time and space.
  • During update operation, ConcurrentHashMap only lock a bucket on which update is happening instead of whole Map.
  • All Operations (read,write,delete,update)of ConcurrentHaskMap are thread-safe.

Sample Code :

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentHashMapDemo implements Runnable{

/**
* @param args
*/
private String threadName;
    private static Map<String,String> cMapObject=new ConcurrentHashMap<String,String>();
 
    ConcurrentHashMapDemo(String threadName){
    cMapObject.put("1","java");
    cMapObject.put("2","lang");
    cMapObject.put("3","advance");
this.threadName=threadName;
 }
 public void run() {
try{
 Iterator<String> it = cMapObject.keySet().iterator();
   while(it.hasNext()){
String key=it.next();
cMapObject.put("NewKey"+key, "NewValue"+key);
System.out.println(cMapObject.get("NewKey"+key));
 }
 System.out.println(threadName +" execution completed.");
 
}catch(Exception e){
 e.printStackTrace();
}finally{
}
 }
 
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService  executor= Executors.newCachedThreadPool();
 executor.execute(new ConcurrentHashMapDemo("First Thread"));
 executor.execute(new ConcurrentHashMapDemo("Second Thrad"));
 executor.shutdownNow();
 }

}

output :

{1=java, NewKey1=NewValue1, NewKeyNewKey1=NewValueNewKey1, NewKey2=NewValue2, NewKeyNewKey2=NewValueNewKey2, NewKeyNewKey3=NewValueNewKey3, NewKey3=NewValue3, 3=advance, 2=lang}



Note : Here in the above sample code writing and reading of the ConcurrentHashMap object is happening simultaneously.However same operation can give ConcurrentModification exception in HashMap.




3 comments:

  1. Is there any statistic which prove 16 locks is optimal? Lets say there are 20 thread modifying concurrent hash map having 20 bucket concurrently. How this is going to work?

    ReplyDelete
  2. In the ConcurrentHashMap Api from Oracle documentations, you will find the following constants which has default value 16.

    static final int DEFAULT_INITIAL_CAPACITY = 16;
    static final int DEFAULT_CONCURRENCY_LEVEL = 16;

    To get the best performance from ConcurrentHashMap you should choose a value to accommodate as many threads as will require in your application to concurrently modify the table. Using a significantly higher value than you need can waste space and time, and a significantly lower value can lead to thread contention.

    Access times to retrieve and save data from CHM (ConcurrentHashMap ) for a range of thread levels are below 10 microseconds. I have tested up to 500 concurrent threads which gives good performance results.


    [ Lets say there are 20 thread modifying concurrent hash map having 20 bucket concurrently. How this is going to work? ]


    Default concurrency level of ConcurrentHashMap is 16, and accordingly Map is divided into 16 part and each part is governed with different lock.
    If 20 thread modifying ConcurrentHashMap having concurrency level 16 then only 16 thread can modify the concurrent hash map simultaneously rest 4 thread will wait to get lock released from other thread.
    If 2 threads is trying to put object in the bucket and finds a bucket empty, it will create a new Node and compare-and-swap it into place. If this process is successful, the operation is considered as completed.
    If two threads are attempting to add nodes into the same bucket more-or-less simultaneously, the insertion for the first will succeed, and it will fail for the second thread.
    The thread whose compare and swap has failed simply goes around the loop and retries. In fact, all the other write operations are within a loop.
    IN ConcurrentHashMap, retrieval operations generally do not block the head of bucket, but may overlap with update operations.If you trying to get the object from the bucket you will get latest updated value from the bucket

    ConcurrenthashMap gives good performance if your application have more read operation than write. It is good choice for caching.

    ReplyDelete