也许我发现了concurrenthashmap的错误

时间:2017-05-16 12:16:06

标签: java-8 concurrenthashmap

当我想有效地实现^ b时,我使用并发hashmap来存储计算值,代码是

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));
}

然后我称这个方法

pow(2, 30);

但输出后

2 ^ 30
2 ^ 15
2 ^ 7

它被阻止,使用jstack -l pid我得到以下信息

"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)

起初我怀疑可能发生了死锁,然后在追踪ConcurrentHashmap的源代码后,我知道它实际上是死循环。 当密钥为2,3时,它与密钥9具有相同的索引2,15,但fh(fh = f.hash)为-3,无法满足< / p>

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

所以在这种情况下,它无法打破循环

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

并无限循环。

是故障还是故意设计?

1 个答案:

答案 0 :(得分:2)

正如C-Otto已经评论过,您在第一个computeIfAbsent()方法调用中调用了第二个computeIfabsent()The documentation for this method明确指出:

  

计算正在进行时,其他线程可能会阻止此地图上的某些尝试更新操作,因此计算应该简短,并且不得尝试更新此映射的任何其他映射。

所以这不是ConcurrentHashMap实现中的错误,而是代码中的错误。