Javadoc clearly explains the reason:
Some attempts to perform update operations on this map by other threads may be blocked while the calculation is in progress , so the calculation should be short and simple, and should not try to update any other mappings of this map.
You should not forget that ConcurrentHashMap designed to provide the use of a stream-safe card without blocking, as is the case for older thread-safe card classes like HashTable .
When a modification occurs on the map, it only blocks the corresponding display, and not the entire map.
ConcurrentHashMap - a hash table that supports full concurrency search queries and expected concurrency for updates.
computeIfAbsent() is a new method added in Java 8.
If it is poorly used, that is, if in the body of computeIfAbsent() , which already blocks the matching of the key passed to the method, you block another key, you enter the path where you can defeat the goal of ConcurrentHashMap as finally you will block two displays.
Imagine a problem if you block a larger display inside computeIfAbsent() and that this method is not short. concurrency access on the map will become slow.
So, javadoc computeIfAbsent() emphasizes this potential problem, recalling the principles of ConcurrentHashMap : keep it simple and fast.
Here is a sample code illustrating the problem.
Suppose we have an instance of ConcurrentHashMap<Integer, String> .
We will start two threads that use it:
- First thread:
thread1 , which calls computeIfAbsent() with key 1 - Second thread:
thread2 , which calls computeIfAbsent() with key 2
thread1 performs a fairly fast task, but does not follow the instructions of computeIfAbsent() javadoc: it updates key 2 in computeIfAbsent() , that is, another mapping that is used in the current context of the method (i.e. key 1 ).
thread2 performs a rather long task. It calls computeIfAbsent() using key 2 , following the recommendations of javadoc: it does not update any other mapping in its implementation.
To simulate a lengthy task, we can use the Thread.sleep() method with parameter 5000 .
For this particular situation, if thread2 starts before thread1 , call map.put(2, someValue); in thread1 will be blocked, and thread2 will not be returned computeIfAbsent() , which blocks the display of key 2 .
Finally, we get an instance of ConcurrentHashMap that blocks the display of key 2 for 5 seconds, while computeIfAbsent() is called with the display of key 1 .
This is misleading, inefficient, and goes against the intent of ConcurrentHashMap and the description of computeIfAbsent() , which intent calculates the value for the current key:
if the specified key is not yet associated with the value, try to calculate its value using this mapping function and enter it on this map if null
Code example:
import java.util.concurrent.ConcurrentHashMap; public class BlockingCallOfComputeIfAbsentWithConcurrentHashMap { public static void main(String[] args) throws InterruptedException { ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>(); Thread thread1 = new Thread() { @Override public void run() { map.computeIfAbsent(1, e -> { String valueForKey2 = map.get(2); System.out.println("thread1 : get() returns with value for key 2 = " + valueForKey2); String oldValueForKey2 = map.put(2, "newValue"); System.out.println("thread1 : after put() returns, previous value for key 2 = " + oldValueForKey2); return map.get(2); }); } }; Thread thread2 = new Thread() { @Override public void run() { map.computeIfAbsent(2, e -> { try { Thread.sleep(5000); } catch (Exception e1) { e1.printStackTrace(); } String value = "valueSetByThread2"; System.out.println("thread2 : computeIfAbsent() returns with value for key 2 = " + value); return value; }); } }; thread2.start(); Thread.sleep(1000); thread1.start(); } }
As a conclusion, we always get:
thread1: get () returns with a value for key 2 = null
thread2: computeIfAbsent () returns with a value for key 2 = valueSetByThread2
thread1: after put () returns, the previous value for key 2 = valueSetByThread2
This is written quickly, since reads on ConcurrentHashMap not blocked:
thread1: get () returns with a value for key 2 = null
but this:
thread1: after put () returns, the previous value for key 2 = valueSetByThread2
only output when thread2 returns computeIfAbsent() .