ConcurrentHashMap BUG 死锁

 

BUG 代码:

package com.zl.map.concurrent;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapBug {
    public static void main(String[] args) {

        Map<String, Integer> map = new ConcurrentHashMap<>(16);
        map.computeIfAbsent(
                "AaAa",
                key -> {
                    return map.computeIfAbsent(
                            "BBBB",
                            key2 -> 42);
                }
        );


        System.out.println("======  end  =============");
    }
}

 

执行上面的代码片段会产生死锁。当map中不存在key="AaAa"时,computeIfAbsent会插入该key,并将以下lamda函数的返回值(42)作为它的value。

而这个lamda函数其实会继续去对key="BBBB"的Node进行同样操作,并设置value=42。但是由于这里的“AaAa”和“BBBB”这个字符串的hashCode一样,导致执行出现死锁
 

 

 

问题出在方法:     public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {

代码上有注释:    must not attempt to update any other mappings of this map.

 

这句话确定了这个问题应该是已知存在的。
所以应该绝对避免在computeIfAbsent中有递归,或者修改map的任何操作

 

为了搞清楚原因,我们继续debug concurrentHashMap的源码,发现这种在computeIfAbsent中,

如果尝试修改map的情况下,代码会发生死循环.

 

 

 

由于concurrentHashMap中使用的是cas操作,因此在出现cas嵌套的情况下,就会形成一种『死锁』。举例来说,一个值原来是 1, 我想把它修改成2,正常的cas操作,会比较在修改的那一刻,值是否仍然为1。这种比较,在cas只有一层的情况下,是没有问题的。但是,假如有两层cas,这个值原来是1,第一层把 1 -> 2,在cas还没有生效时,继续进入第二层cas操作,把 2 -> 3,当最终提交时,第二层cas比较当前值是否是2,但由于当前指仍然是1,因此修改无效。最终反复进入循环,形成死锁。

 

在JDK1.8中使用ConcurrentHashMap时,不要在computeIfAbsent的lambda函数中再去执行更新其它节点value的操作。

 

在 JDK1.9 之后修复 ?

 

This is fixed in JDK 9 with JDK-8071667 . When the test case is run in JDK 9-ea, it gives a ConcurrentModification Exception.
java 9

 

 

参考:

https://blog.csdn.net/lx1848/article/details/81256443

https://stackoverflow.com/questions/43861945/deadlock-in-concurrenthashmap

https://www.jianshu.com/p/59bd27e137e1

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值