先看這么一段代碼:
Map map =newHashMap<>();
map.computeIfAbsent("a",key -> {
? ? ? ?map.put("a","v2");
? ? ? ? return"v1";
});
這段代碼執(zhí)行以后"a"對應(yīng)的value到底是多少呢?
答案是執(zhí)行這行代碼的線程cpu占用會到100%,而且程序不退出。查看線程堆棧出現(xiàn)這樣的情況:
"main" #1 prio=5 os_prio=31 tid=0x00007f804f002000 nid=0x1703 runnable [0x0000700000218000]
java.lang.Thread.State: RUNNABLE
at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1069)
at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
at com.wangqun.HelloComputeIfAbsent.lambda$main$0(HelloComputeIfAbsent.java:14)
at com.wangqun.HelloComputeIfAbsent$$Lambda$1/796533847.apply(Unknown Source)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
- locked <0x000000076b448b10> (a java.util.concurrent.ConcurrentHashMap$ReservationNode)
at com.wangqun.HelloComputeIfAbsent.main(HelloComputeIfAbsent.java:13)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
可以看到put方法和computeIfAbsent方法同時卡在了一個ReservationNode對象上。查看ConcurrentHashMap的源碼可以發(fā)現(xiàn),這種情況在bucket沒有初始化的時候會發(fā)生,簡單來說computeIfAbsent會在bucket為null的時候初始化一個ReservationNode來占位,然后等待后面的計算結(jié)果出來,再替換當(dāng)前的占位對象,而putVal會synchorized這個對象,并根據(jù)其hash值的正負(fù)來進(jìn)行更新,遺憾的時ReservationNode的hash是-3,在putVal中沒有處理過這種情況,然后就一直for循環(huán)處理了。
這其實是一種編程bug,computeIfAbsent在使用的時候,計算value的過程中一定不能出現(xiàn)對map的修改操作,否則如果修改的key和computeIfAbsent的key分到同一個桶,而且那個bucket沒有被使用過,就會悲劇。
如果非要在計算新值的過程中修改map,可以換一種方法來實現(xiàn)computeIfAbsent的功能:
V value = map.get(k);
if (value == null) {
? ? V newValue = computeValue(k); ?// 這里對computeValue(k)的重復(fù)調(diào)用不敏感
? ? value = map.putIfAbsent(k, newValue);
? ?if (value == null) {
? ? ? ? return newValue;
? ?}
? ?return value;
}
對于HashMap.computeIfAbsent,這么調(diào)用則沒有這種問題出現(xiàn)。