6

I have a Java 8 web application running on Apache Tomcat 9. The invocation of ConcurrentHashMap's computeIfAbsent() method is not returning or is taking too long to return.

In the code given below, the line 'Adding to Map' is printed and the line 'Map : ' does not print at all in some cases as if the executing thread is trapped within the method. Once it gets trapped any subsequent calls to the same method with the same id also get stuck and never return while calls with a different id return immediately. Testing on another instance with a different id, the computeIfAbsent() method returned after 2 minutes. The maximum concurrent calls executing the code at the time of testing would be around 20 only. As per my understanding computeIfAbsent() is thread safe. What is wrong here?

private Map<String, Map<String, SomeClass>> aMap = new ConcurrentHashMap<>();
LOGGER.debug("Adding to Map");
Map<String, SomeClass> m = aMap
            .computeIfAbsent(id, k -> Collections.synchronizedMap(new HashMap<>()));
LOGGER.debug("Map : " + m);
Nick
  • 222
  • 1
  • 2
  • 10
  • 1
    Some other thread is blocking you from inserting... – Michael Jul 16 '19 at 12:42
  • 1
    "while calls with different id returned immediately" Different buckets – Michael Jul 16 '19 at 12:43
  • @Michael, i am looking for the answer for "What happened to the bucket the problematic `id` is pointing to?" – Nick Jul 16 '19 at 14:12
  • *As per my understanding computeIfAbsent() is thread safe* - that is _exactly_ the reason why they are stuck to begin with. – Eugene Jul 17 '19 at 07:26
  • 1
    You might want to try running `jstack` to see where exactly it's stuck -- and maybe even to see if some other thread is performing some more-expensive operation on the same map. – ruakh Jul 18 '19 at 06:20

1 Answers1

1

Any subsequent calls to the same method with same id also got stuck and never returned while calls with different id returned immediately ?

Yes, If the computation is in progress any subsequent computation calls of that id will be blocked

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. The entire method invocation is performed atomically, so the function is applied at most once per key. Some attempted update operations on this map by other threads may be blocked while computation is in progress so the computation should be short and simple, and must not attempt to update any other mappings of this map.

The maximum concurrent calls executing the code at the time of testing would be around 20 only. As per my understanding ?

No, It completely depends on how many buckets are available in that map

In ConcurrentHashMap, at a time any number of threads can perform retrieval operation but for updation in object, thread must lock the particular segment in which thread want to operate.This type of locking mechanism is known as Segment locking or bucket locking.Hence at a time 16 updation operations can be performed

computeIfAbsent() is thread safe ?

Yes, it is thread safe ConcurrentHashMap

A hash table supporting full concurrency of retrievals and high expected concurrency for updates. This class obeys the same functional specification as Hashtable, and includes versions of methods corresponding to each method of Hashtable. However, even though all operations are thread-safe, retrieval operations do not entail locking, and there is not any support for locking the entire table in a way that prevents all access. This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.

Honestly i'm not the one who designed and implemented ConcurrentHashMap, but through the internet i found an article for java 8 ConcurrentHashMap improvements, I assume this might causing the delay in first call.

Lazy table initialization that minimizes footprint until first use

Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • I understand that the subsequent method calls are blocked because the first call is blocked. The question is why the first call got blocked considering the mapping function is short and simple and not updating any other mappings of the Map. – Nick Jul 16 '19 at 14:07
  • actually how did you measure the time @Nikhil? and I'm not sure what happened internally for the first call, may be you should try running it for multiple times and check this has been repeating every time – Ryuzaki L Jul 16 '19 at 14:11
  • I measured the time by getting the difference between two log trace time stamps. It is not reproducible always. But a couple of time it happened and as i mentioned before once it returned after 2 minutes. – Nick Jul 16 '19 at 14:16
  • How big is that `Map` size? and which version jdk are you on? if it is a bug it should occur every time @Nikhil – Ryuzaki L Jul 16 '19 at 14:19
  • JRE8. I don't know the exact size of the Map but i am sure it will be less than 20. – Nick Jul 16 '19 at 14:23
  • check updated answer and this to https://medium.com/@sergeykuptsov/how-it-works-in-java-concurrenthashmap-7fff8722bb72 – Ryuzaki L Jul 16 '19 at 14:39