Why JDK 8’s computeIfAbsent Can Deadlock: A Deep Dive into the Bug
This article explores a JDK 8 bug in ConcurrentHashMap’s computeIfAbsent method that can cause an infinite loop, explains how the issue was discovered, reproduces it with test code, analyzes the root cause, and shows the fix introduced in JDK 9.
JDK Bug Overview
The author discovered a bug in JDK 8 while reviewing the Dubbo 2.7.7 release notes, where a red‑boxed entry in the Bugfixes list turned out to be a JDK issue rather than a Dubbo problem.
It concerns the java.util.concurrent.ConcurrentHashMap#computeIfAbsent method, which can enter an endless loop under certain conditions.
Reproducing the Bug
A test case from the OpenJDK bug tracker (JDK‑8062841) is used to demonstrate the problem. Running the test on JDK 8 shows that the method never returns because it gets stuck in a dead loop.
Understanding computeIfAbsent
The method’s purpose is to compute a value for a missing key using a mapping function and store the result if it is non‑null.
When the mapping function itself calls
computeIfAbsenton the same map with a key that hashes to the same bucket, a recursive call occurs.
Root Cause Analysis
Both keys “AaAa” and “BBBB” produce the same hash after the spread operation (2031775), causing them to target the same bucket.
The first call inserts a
ReservationNodeas a placeholder. The second call encounters this placeholder, leading to a for‑loop at line 1649 that never breaks, resulting in an infinite loop.
Bug Fix in JDK 9
In JDK 9 the loop was fixed by adding a check for
ReservationNodeand throwing an
IllegalStateExceptionwhen such a node is encountered.
Takeaways
The bug illustrates that even thread‑safe containers like
ConcurrentHashMapcan be misused, leading to deadlocks or infinite loops.
When using
computeIfAbsent, avoid recursive mappings that may target the same bucket.
For JDK 8 users, a workaround is to use
getfollowed by
putIfAbsentto achieve the same effect without triggering the bug.
<code>public class Test {
static Map<Integer, Integer> cache = new ConcurrentHashMap<>();
public static void main(String[] args) {
System.out.println("f(" + 14 + ") =" + fibonacci(14));
}
static int fibonacci(int i) {
if (i == 0) return i;
if (i == 1) return 1;
return cache.computeIfAbsent(i, key -> {
System.out.println("Slow calculation of " + key);
return fibonacci(i - 2) + fibonacci(i - 1);
});
}
}</code>macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.