Backend Development 16 min read

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.

macrozheng
macrozheng
macrozheng
Why JDK 8’s computeIfAbsent Can Deadlock: A Deep Dive into the Bug

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

computeIfAbsent

on 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

ReservationNode

as 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

ReservationNode

and throwing an

IllegalStateException

when such a node is encountered.

Takeaways

The bug illustrates that even thread‑safe containers like

ConcurrentHashMap

can 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

get

followed by

putIfAbsent

to 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>
JDKThread SafetybugConcurrentHashMapJava8computeIfAbsent
macrozheng
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.