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 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";
   }

 

你可能感兴趣的:(java)