Share
    putIfAbsent vs ComputeIfAbsent 两者的区别

    如果key 不存在:
    那么putIfAbsent 和 ComputeIfAbsent 的作用几乎是一样的

    如果key 存在:
    putIfAbsent : 将可以对应的值返回, 如果获取value 的消耗很大,那么就会有性能问题
    computeIfAbsent : 本质上就是通过回调函数,来决定获取value 的逻辑,这样对性能有更大的调控能力。
    举个例子: 假设 “key1” 对应了一个很重value 对象, 如果我只是希望 如果存在就直接返回一个null 或者比value更轻量的对象。 那么就可以直接通过computeIfAbsent 的回调函数来实现。

    一个结合 LongAdder 的例子:

    1. private Map<String, Long> gooduse() throws InterruptedException {
    2. ConcurrentHashMap<String, LongAdder> freqs = new ConcurrentHashMap<>(ITEM_COUNT);
    3. ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUNT);
    4. forkJoinPool.execute(() -> IntStream.rangeClosed(1, LOOP_COUNT).parallel().forEach(i -> {
    5. String key = "item" + ThreadLocalRandom.current().nextInt(ITEM_COUNT);
    6. freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
    7. }
    8. ));
    9. forkJoinPool.shutdown();
    10. forkJoinPool.awaitTermination(1, TimeUnit.HOURS);
    11. return freqs.entrySet().stream()
    12. .collect(Collectors.toMap(
    13. e -> e.getKey(),
    14. e -> e.getValue().longValue())
    15. );
    16. }
    17. }

    在第6行, 当key 对应的值是空的时候,就会将LongAdder 赋值进去,然后返回,这样即便是多个线程的操作都可以统一操作LongAdder 这个对象,也可以统一调用 increment 的方法,保证线程安全

    Review:
    ForkjoinPool vs ExecutorService 的线程池性能 比较的文章, 从本质上看,两者的使用方式几乎是一致的, 但ForkjoinPool 的调度性能比ExecutorService 更优秀

    Tips:
    PlantUML 的component Diagram 可以很方便地画架构图,而且能通过版本控制进行发布