美文网首页
ConcurrentHashMap.computeIfAbsen

ConcurrentHashMap.computeIfAbsen

作者: blowyourheart | 来源:发表于2016-10-28 16:21 被阅读0次

先看这么一段代码:

Map map =newHashMap<>();

map.computeIfAbsent("a",key -> {

       map.put("a","v2");

        return"v1";

});

这段代码执行以后"a"对应的value到底是多少呢?

答案是执行这行代码的线程cpu占用会到100%,而且程序不退出。查看线程堆栈出现这样的情况:

"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的源码可以发现,这种情况在bucket没有初始化的时候会发生,简单来说computeIfAbsent会在bucket为null的时候初始化一个ReservationNode来占位,然后等待后面的计算结果出来,再替换当前的占位对象,而putVal会synchorized这个对象,并根据其hash值的正负来进行更新,遗憾的时ReservationNode的hash是-3,在putVal中没有处理过这种情况,然后就一直for循环处理了。

这其实是一种编程bug,computeIfAbsent在使用的时候,计算value的过程中一定不能出现对map的修改操作,否则如果修改的key和computeIfAbsent的key分到同一个桶,而且那个bucket没有被使用过,就会悲剧。

如果非要在计算新值的过程中修改map,可以换一种方法来实现computeIfAbsent的功能:

V value = map.get(k);

if (value == null) {

    V newValue = computeValue(k);  // 这里对computeValue(k)的重复调用不敏感

    value = map.putIfAbsent(k, newValue);

   if (value == null) {

        return newValue;

   }

   return value;

}

对于HashMap.computeIfAbsent,这么调用则没有这种问题出现。

相关文章

网友评论

      本文标题:ConcurrentHashMap.computeIfAbsen

      本文链接:https://www.haomeiwen.com/subject/clreuttx.html