并发工具类库使用的常见问题

一、ThreadLocal在多线程环境中没有清理

由于ThreadLocal是和线程绑定的,如果线程被复用了,也即使用了线程池,那么ThreadLocal中的值是可能被复用的,这个特性如果是开发者没有预料到的,那么会产生很大的问题。例如,在JAVA web应用中,我们通常会使用很多ThreadLocal来保存一次请求的不同上下文信息,其中就包含登录用户,如果一次请求完成后,没有清理掉当前登录用户信息,那么当另外一个用户的请求进来,就会使用上一个用户,如果是涉及到数据写入,将直接导致数据错乱,造成严重生产问题。

二、ConcurrentHashMap使用的问题

ConcurrentHashMap提供的能力是保证单个操作在多线程环境下是安全的。如果有一段逻辑是先读取size大小,在决定是否往map里put(),那么这段逻辑必然产生并发问题。因为错误理解的ConcurrentHashMap的能力。解决办法是用加锁的方式控制并发。

三、CopyOnWriteArrayList使用的问题

CopyOnWrite 是一个常用的技术,Linux、Redis中都用到了。在 Java 中,CopyOnWriteArrayList 虽然是一个线程安全的 ArrayList,但因为其实现方式是,每次修改数据时都会复制一份数据出来,所以有明显的适用场景,即读多写少或者说希望无锁读的场景。

//测试并发写的性能
@GetMapping("write")
public Map testWrite() {
List copyOnWriteArrayList = new CopyOnWriteArrayList<>();
List synchronizedList = Collections.synchronizedList(new ArrayList<>());
StopWatch stopWatch = new StopWatch();
int loopCount = 100000;
stopWatch.start("Write:copyOnWriteArrayList");
//循环100000次并发往CopyOnWriteArrayList写入随机元素
IntStream.rangeClosed(1, loopCount).parallel().forEach(__ -> copyOnWriteArrayList.add(ThreadLocalRandom.current().nextInt(loopCount)));
stopWatch.stop();
stopWatch.start("Write:synchronizedList");
//循环100000次并发往加锁的ArrayList写入随机元素
IntStream.rangeClosed(1, loopCount).parallel().forEach(__ -> synchronizedList.add(ThreadLocalRandom.current().nextInt(loopCount)));
stopWatch.stop();
log.info(stopWatch.prettyPrint());
Map result = new HashMap();
result.put("copyOnWriteArrayList", copyOnWriteArrayList.size());
result.put("synchronizedList", synchronizedList.size());
return result;
}

//帮助方法用来填充List
private void addAll(List list) {
list.addAll(IntStream.rangeClosed(1, 1000000).boxed().collect(Collectors.toList()));
}

//测试并发读的性能
@GetMapping("read")
public Map testRead() {
//创建两个测试对象
List copyOnWriteArrayList = new CopyOnWriteArrayList<>();
List synchronizedList = Collections.synchronizedList(new ArrayList<>());
//填充数据
addAll(copyOnWriteArrayList);
addAll(synchronizedList);
StopWatch stopWatch = new StopWatch();
int loopCount = 1000000;
int count = copyOnWriteArrayList.size();
stopWatch.start("Read:copyOnWriteArrayList");
//循环1000000次并发从CopyOnWriteArrayList随机查询元素
IntStream.rangeClosed(1, loopCount).parallel().forEach(__ -> copyOnWriteArrayList.get(ThreadLocalRandom.current().nextInt(count)));
stopWatch.stop();
stopWatch.start("Read:synchronizedList");
//循环1000000次并发从加锁的ArrayList随机查询元素
IntStream.range(0, loopCount).parallel().forEach(__ -> synchronizedList.get(ThreadLocalRandom.current().nextInt(count)));
stopWatch.stop();
log.info(stopWatch.prettyPrint());
Map result = new HashMap();
result.put("copyOnWriteArrayList", copyOnWriteArrayList.size());
result.put("synchronizedList", synchronizedList.size());
return result;
}

你可能感兴趣的:(JAVA开发常见错误示例,java,开发语言)