文章阅读-11周-两篇英文文章阅读

本周英语文章阅读
What are Java servlets? Request handling for Java web applications
这篇数据非常基础知识文章,对于已经有java web或有servlet知识来说属于非常易懂。这篇文章当做额外英语文章学习。

ConcurrentHashMap: Call Only One Method Per Key
本文章主介绍在使用ConcurrentHashMap 的两种情况
1、调用ConcurrentHashMap 不同的方法操作一个key会导致竞争问题。
2、递归调用ConcurrentHashMap 相同的方法操作不同key会导致死锁。

问题一

public class TestUpdateWrong {
    private final ConcurrentHashMap<Integer,Integer>  map = new  ConcurrentHashMap<Integer,Integer>();
    @Interleave(group=TestUpdateWrong.class,threadCount=2)
    public void update()  {
            Integer result = map.get(1);        
            if( result == null )  {
                map.put(1, 1);
            }
            else    {
                map.put(1, result + 1 );
            }   
    }
    @Test
    public void testUpdate() throws InterruptedException    {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute( () -> { update();   }  );
        executor.execute( () -> { update();   }  );
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.MINUTES);
    }   
    @After
    public void checkResult() {
        assertEquals( 2 , map.get(1).intValue() );
    }   
}

执行之后会报错,因为两个线程并发执行时候,可能存同时获取result都为null,所以都进行put操作。

应该update调整为以下代码

public void update() {
    map.compute(1, (key, value) -> {
        if (value == null) {
            return 1;
        } else {
            return value + 1;
        }
    });
}

问题二

public class TestUpdateRecursive {
    private final ConcurrentHashMap<Integer, Integer> map = 
            new ConcurrentHashMap<Integer, Integer>();
    public TestUpdateRecursive() {
        map.put(1, 1);
        map.put(2, 2);
    }
    public void update12() {
        map.compute(1,  (key,value) ->   {   
               map.compute(2, ( k , v ) ->  {  return 2; }  );   
               return 2;       
        });
    }   
    public void update21() {
        map.compute(2,  (key,value) ->   {   
               map.compute(1, ( k , v ) ->  {  return 2; }  );   
               return 2;       
        });
    }
    @Test
    public void testUpdate() throws InterruptedException    {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute( () -> { update12();   }  );    
        executor.execute( () -> { update21();   }  );
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.MINUTES);
    }   
}

执行该代码会报错


16933969-74f140fd8264ea2a.png
image.png

原因
ConcurrentHashMap 使用数组存储keys和values映射关系,每次我们需要更新映射关系,会对该元素加上锁。compute 使用之后会导致对该key进行锁的操作。以上逻辑两个线程分别锁1和2,同时又想获取对方key值,所以知道死锁。
只有在更新才会锁元素,例如只读get不会使用锁操作。在compute调用里面使用get是没有问题的。

转载于:https://www.jianshu.com/p/a9f4f25a5b03

猜你喜欢

转载自blog.csdn.net/weixin_33940102/article/details/91069401