ConcurrentHashMap中computeIfAbsent递归调用导致死循环

原因是:

map.computeIfAbsent(key1, mappingFunction)

如果当前key1-hash对应的tab位(可以理解为槽)刚好是空的,在计算mappingFunction之前会
step1: 先往对应位置放一个ReservationNode占位
step2: 然后计算mappingFunction的值value, 
step3: 再将value组装成最终NODE, 把占位的ReservationNode换成最终NODE;

这时如果:
 mappingFunction 中用到了 当前map的computeIfAbsent方法, 很不巧 key2-hash的槽为和key1的是同一个,
因为key1已经在槽中放入了占位节点, 在处理key2时候for循环的所以处理条件都不符合 程序进入了死循环

但是如果:  

key2-hash的槽位和key1的不一样, 是不会发生死循环

 

多线程问题:

因为ConcurrentHashMap在处理上述step1-step3是同步的,  而且在处理时候会同步获取的值, 所以是不存在线程不安全的, 纯粹是当前线程死循环

Thread1 通过cas 在槽x放了个ReservationNode(RN1), 然后假设mappingFunction执行的很慢

Thread2 在槽x和Thread竞争, cas失败没有抢到占位符; 进行下一轮for循环, 这是因为槽x中已经被放置了RN1, 所以Thread2获取到这个RN1,在执行synchronized(RN1) 时候被thread1block住

code sample:

public static void main(String[] args) throws IOException {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        //caseA: dead loop
//        map.computeIfAbsent("AA", key -> map.computeIfAbsent("BB", k->"bb"));

        //caseB: block, but no dead loop
        new Thread(()->map.computeIfAbsent("AA", key -> waitAndGet())).start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);  //delay 1 second
            } catch (InterruptedException e) {}
            map.computeIfAbsent("BB", key-> "bb");
        }).start();

    }

   private static String waitAndGet(){
       try {
           TimeUnit.SECONDS.sleep(20);
       } catch (InterruptedException e) {
       }
       return "AAA";
   }

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值