ConcurrentHashMap和Fibonacci数字 - 结果不一致

时间:2017-05-04 05:58:53

标签: java fibonacci concurrenthashmap

我用ConcurrentHashMapcomputeIfAbsent()方法编写了一个递归计算斐波纳契数的程序:

当我使用像8,9,10这样的小值时,程序运行得非常好,但是当值从10 to 20增加时,程序会无限循环程序永远不会停止

 public class Test {
    static Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        System.out.println("Fibonacci result for 20 is" + fibonacci(20));
    }

    static int fibonacci(int i) {
        if (i == 0)
            return i;

        if (i == 1)
            return 1;

        return concurrentMap.computeIfAbsent(i, (key) -> {
            System.out.println("Value is " + key);
            return fibonacci(i - 2) + fibonacci(i - 1);
        });
    }
}

有人可以告诉我为什么它永远被卡住了吗?

4 个答案:

答案 0 :(得分:27)

你正陷入僵局。

ConcurrentHashMap上的

computeIfAbsent将锁定相应密钥将进入的存储桶。如果您尝试计算的Fibonacci高于映射中的桶数,则递归调用将尝试锁定已在锁定堆栈中进一步锁定的存储桶。但是,当然,在所有递归调用完成之前,不能释放该锁。因此,你的僵局。

我会重新考虑你在这里使用ConcurrentHashMap的决定。

答案 1 :(得分:3)

这种计算斐波那契数的递归方法具有指数复杂性。使用缓存可以将其减少回线性,或者您可以使用简单循环而不是递归来获得线性算法。

我想知道你为什么使用ConcurentHashMap进行缓存。我会使用简单的map或数组进行缓存。

当数值被稀疏时,地图对数组有优势,但是当你有数字序列时,你可以使用简单的数组。

答案 2 :(得分:3)

我接受了线程转储,我们可以看到锁定 0x000000076b70bba0 的线程导致死锁问题。

如果我错了,请纠正我。

main - priority:5 - threadId:0x00000000021af000 - nativeId:0x2798 - state:RUNNABLE
    stackTrace:
    java.lang.Thread.State: RUNNABLE
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1674)
    - locked <0x000000076b70bba0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70c720> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70c5c0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70c460> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70c300> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70c1a0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70c040> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70bee0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.lambda$0(Test.java:20)
    at Test$$Lambda$1/834600351.apply(Unknown Source)
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
    - locked <0x000000076b70bba0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
    at Test.fibonacci(Test.java:18)
    at Test.main(Test.java:8)
    Locked ownable synchronizers:
    - None

答案 3 :(得分:1)

根据Oracle Doc

  

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

正如 Joe C 在最顶层的答案中正确地说的那样,ConcurrentHashMap的默认初始化在实例化时分配了少量的桶。

使用ConcurrentHashMap的目的是允许从多个线程并发地修改Map而无需阻塞它们(link)。

如果您仍希望继续使用ConcurrentHashMap作为应用程序,那么我建议在创建期间增加initialCapacity。

static Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>(11);

可以计算斐波纳契数列,包括25

根据文件,

  

ConcurrentHashMap()
  使用默认的初始表大小(16)创建一个新的空映射。

但是,我不同意,因为我注意到默认大小要小得多。
这样做的原因是当你从fibonacci(25)获得ConcurrentHashMap<>(11)然后ConcurrentHashMap<>()&lt; - 这里应该默认为16 ..但它不是。