本周英语文章阅读
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);
}
}
执行该代码会报错
image.png
原因
ConcurrentHashMap 使用数组存储keys和values映射关系,每次我们需要更新映射关系,会对该元素加上锁。compute 使用之后会导致对该key进行锁的操作。以上逻辑两个线程分别锁1和2,同时又想获取对方key值,所以知道死锁。
只有在更新才会锁元素,例如只读get不会使用锁操作。在compute调用里面使用get是没有问题的。