我用ConcurrentHashMap
和computeIfAbsent()
方法编写了一个递归计算斐波纳契数的程序:
当我使用像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);
});
}
}
有人可以告诉我为什么它永远被卡住了吗?
答案 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)
计算正在进行时,其他线程可能会阻止此地图上的某些尝试更新操作,因此计算应该简短,并且不得尝试更新此地图的任何其他映射
正如 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 ..但它不是。