并发给ConcurrentHashMap赋值
低性能
使用 ConcurrentHashMap 来统计,Key 的范围是 10。
使用最多 10 个并发,循环操作 1000 万次,每次操作累加随机的 Key。
如果 Key 不存在的话,首次设置值为 1。
private Map<String, Long> normaluse() throws InterruptedException {
ConcurrentHashMap<String, Long> freqs = new ConcurrentHashMap<>(10);
ForkJoinPool forkJoinPool = new ForkJoinPool(10);
forkJoinPool.execute(() -> IntStream.rangeClosed(1, 10000000)
.parallel().forEach(i -> {
String key = "item" + ThreadLocalRandom.current().nextInt(10);
synchronized (freqs) {
if (freqs.containsKey(key)) {
freqs.put(key, freqs.get(key) + 1);
} else {
freqs.put(key, 1L);
}
}
}
));
forkJoinPool.shutdown();
forkJoinPool.awaitTermination(1, TimeUnit.HOURS);
return freqs;
}
改进
使用 ConcurrentHashMap 的原子性方法 computeIfAbsent 来做复合逻辑操作,判断 Key 是否存在 Value,如果不存在则把 Lambda 表达式运行后的结果放入 Map 作为 Value,也就是新创建一个 LongAdder 对象,最后返回 Value。
private Map<String, Long> gooduse() throws InterruptedException {
ConcurrentHashMap<String, LongAdder> freqs = new ConcurrentHashMap<>(10);
ForkJoinPool forkJoinPool = new ForkJoinPool(10);
forkJoinPool.execute(() -> IntStream.rangeClosed(1, 10000000)
.parallel().forEach(i -> {
String key = "item" + ThreadLocalRandom.current().nextInt(10);
freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
}
));
forkJoinPool.shutdown();
forkJoinPool.awaitTermination(1, TimeUnit.HOURS);
return freqs.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getKey(),
e -> e.getValue().longValue())
);
}
computeIfAbsent 使用 java 自带的 Unsafe 实现的 CAS。它在虚拟机层面确保了写入数据的原子性,比加锁的效率高得多:
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v) {
return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
测试
优化后的代码,相比使用锁来操作 ConcurrentHashMap 的方式,性能提升 了 10 倍。
public String good() throws InterruptedException {
StopWatch stopWatch = new StopWatch();
stopWatch.start("normaluse");
Map<String, Long> normaluse = normaluse();
stopWatch.stop();
Assert.isTrue(normaluse.size() == ITEM_COUNT, "normaluse size error");
Assert.isTrue(normaluse.entrySet().stream()
.mapToLong(item -> item.getValue()).reduce(0, Long::sum) == LOOP_COUNT
, "normaluse count error");
stopWatch.start("gooduse");
Map<String, Long> gooduse = gooduse();
stopWatch.stop();
Assert.isTrue(gooduse.size() == ITEM_COUNT, "gooduse size error");
Assert.isTrue(gooduse.entrySet().stream()
.mapToLong(item -> item.getValue())
.reduce(0, Long::sum) == LOOP_COUNT
, "gooduse count error");
log.info(stopWatch.prettyPrint());
return "OK";
}
posted on 2025-10-14 23:49 chuchengzhi 阅读(8) 评论(0) 收藏 举报
浙公网安备 33010602011771号