Tuesday, February 14, 2017

ConcurrentHashMap in java

ConcurrentHashMap performs better than Hashtable or synchronized Map because it only locks a portion of Map, 
instead of whole Map, which is the case with Hashtable and synchronized Map (as HashMap is not thread-safe). 
ConcurrentHashMap allows multiple readers to read concurrently without any blocking and the same time maintains 
integrity by synchronizing write operations.

ConcurrentHashMap is introduced as an alternative of Hashtable and provided all functions supported by Hashtable 
with an additional feature called "concurrency level", which allows ConcurrentHashMap to partition Map. It partitions 
Map into different parts based on concurrency level and locking only a portion of Map during updates. 

Default concurrency level is 16, and accordingly Map is divided into 16 parts and each part is governed with a different 
lock. This means, 16 threads can operate on Map simultaneously until they are operating on different part of Map. 
This makes ConcurrentHashMap high performance despite keeping thread-safety intact. Though, it comes with a limitation. 
Since update operations like put(), remove(), putAll() or clear() is not synchronized, sometimes concurrent retrieval may 
not reflect most recent change on Map.

Iterator returned by keySet of ConcurrentHashMap are weekly consistent and they only reflect state of ConcurrentHashMap
and may not reflect any recent change. Iterator of ConcurrentHashMap's keySet is also fail-safe and doesn’t throw 
ConcurrentModificationExceptoin.

Default concurrency level is 16 and can be changed, by providing a number which make sense and work for you while creating 
ConcurrentHashMap. Since concurrency level is used for internal sizing and indicates number of concurrent update without 
contention. Hence if you just have few writer threads to update Map, keeping it low is much better. ConcurrentHashMap also 
uses ReentrantLock to internally lock its segments.

ConcurrentHashMap examples are similar to Hashtable examples, however they have one more method "putIfAbsent()". Many times 
we need to insert entry into Map if it's not present already, and we wrote following kind of code:

synchronized(map){
  if (map.get(key) == null){
      return map.put(key, value);
  } else{
      return map.get(key);
  }
}

Though this code will work fine in HashMap and Hashtable, This won't work in ConcurrentHashMap; because, during put operation 
whole map is not locked, and while one thread is putting value, other thread's get() call can still return null which result 
in one thread overriding value inserted by other thread. Ofcourse, you can wrap whole code in synchronized block and make it 
thread-safe but that will only make your code single threaded. ConcurrentHashMap provides putIfAbsent(key, value) which does 
same thing but atomically and thus eliminates above race condition. 

ConcurrentHashMap is best suited when you have multiple readers and few writers. If writers outnumber reader, or writer is equal 
to reader, than performance of ConcurrentHashMap effectively reduces to synchronized map or Hashtable. Performance of CHM drops, 
because you got to lock all portion of Map, and effectively each reader will wait for another writer, operating on that portion 
of Map. ConcurrentHashMap is a good choice for caches, which can be initialized during application start up and later accessed 
my many request processing threads. CHM is also a good replacement of Hashtable and should be used whenever possible.

Summary:
1. ConcurrentHashMap allows concurrent read and thread-safe update operation. Use this when you have more readers than writers.
2. During the update operation, ConcurrentHashMap only locks a portion of Map instead of whole Map.
3. The concurrent update is achieved by internally dividing Map into the small portion which is defined by concurrency level.
4. Choose concurrency level carefully as a significantly higher number can be a waste of time and space and the lower number 
    may introduce thread contention.
    
5. All operations of ConcurrentHashMap are thread-safe.
6. Since ConcurrentHashMap implementation doesn't lock whole Map, there is chance of read overlapping with update operations 
    like put() and remove(). In that case result returned by get() method will reflect most recently completed operation from there.

7. Iterator returned by ConcurrentHashMap is weekly consistent, fail-safe and never throw ConcurrentModificationException.
8. ConcurrentHashMap doesn't allow null as key or value.
9. During putAll() and clear() operations, the concurrent read may only reflect insertion or deletion of some entries.

Taken from : http://javarevisited.blogspot.com/2013/02/concurrenthashmap-in-java-example-tutorial-working.html#ixzz4YdshMH8E

No comments:

Post a Comment