美文网首页
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