Share
putIfAbsent vs ComputeIfAbsent 两者的区别
如果key 不存在:
那么putIfAbsent 和 ComputeIfAbsent 的作用几乎是一样的
如果key 存在:
putIfAbsent : 将可以对应的值返回, 如果获取value 的消耗很大,那么就会有性能问题
computeIfAbsent : 本质上就是通过回调函数,来决定获取value 的逻辑,这样对性能有更大的调控能力。
举个例子: 假设 “key1” 对应了一个很重value 对象, 如果我只是希望 如果存在就直接返回一个null 或者比value更轻量的对象。 那么就可以直接通过computeIfAbsent 的回调函数来实现。
一个结合 LongAdder 的例子:
private Map<String, Long> gooduse() throws InterruptedException {
ConcurrentHashMap<String, LongAdder> freqs = new ConcurrentHashMap<>(ITEM_COUNT);
ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUNT);
forkJoinPool.execute(() -> IntStream.rangeClosed(1, LOOP_COUNT).parallel().forEach(i -> {
String key = "item" + ThreadLocalRandom.current().nextInt(ITEM_COUNT);
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())
);
}
}
在第6行, 当key 对应的值是空的时候,就会将LongAdder 赋值进去,然后返回,这样即便是多个线程的操作都可以统一操作LongAdder 这个对象,也可以统一调用 increment 的方法,保证线程安全
Review:
ForkjoinPool vs ExecutorService 的线程池性能 比较的文章, 从本质上看,两者的使用方式几乎是一致的, 但ForkjoinPool 的调度性能比ExecutorService 更优秀
Tips:
PlantUML 的component Diagram 可以很方便地画架构图,而且能通过版本控制进行发布