Search code examples
java-8concurrenthashmap

Maybe I've found a bug of concurrenthashmap


When I want to implement a^b efficiently, I used concurrent hashmap to store calculated value, and the code is

private static final ConcurrentHashMap<String,Long> cache = new ConcurrentHashMap();

public long pow(long a, long b){
    System.out.printf("%d ^ %d%n",a,b);
    if(b == 1L){
        return a;
    }
    if( b == 2L){
        return a*a;
    }
    long l = b/2;
    long r = b - l;

    return cache.computeIfAbsent(a+","+l,k->pow(a,l)) * cache.computeIfAbsent(a+","+r,k->pow(a,r));
}

then I call this method

pow(2, 30);

but after outputting

2 ^ 30
2 ^ 15
2 ^ 7

it is blocked , by using jstack -l pid I got below info

"main" #1 prio=5 os_prio=31 tid=0x00007f910e801800 nid=0x1703 runnable [0x0000700000217000]
   java.lang.Thread.State: RUNNABLE
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1718)
    at interview.Pow.pow(Pow.java:28)
    at interview.Pow.lambda$pow$0(Pow.java:28)
    at interview.Pow$$Lambda$1/1807837413.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b72d930> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at interview.Pow.pow(Pow.java:28)
    at interview.Pow.lambda$pow$0(Pow.java:28)
    at interview.Pow$$Lambda$1/1807837413.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b72d060> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at interview.Pow.pow(Pow.java:28)
    at interview.Pow.testPow(Pow.java:32)

At first I doubt maybe deadlock happened, then after tracing source code of ConcurrentHashmap, I know actually it is dead loop. When key is 2,3, it has the same index 9 of key 2,15, but fh(fh = f.hash) is -3, cannot meet

if (fh >= 0) {...}

So in this case, it cannot break the loop

for (Node<K,V>[] tab = table;;) {...}

and loop infinitely.

Is it a bug or designed this so deliberately?


Solution

  • As C-Otto already commented, you invoke a second computeIfAbsent()from within the first computeIfabsent() method call. The documentation for this method clearly states:

    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.

    So this is no bug in the ConcurrentHashMap implementation but in your code.