1

我在以下配置中使用咖啡因:

    Cache<String, String> cache = Caffeine.newBuilder()
                .executor(newWorkStealingPool(15))
                .scheduler(createScheduler())
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .maximumSize(MAXIMUM_CACHE_SIZE)
                .removalListener(this::onRemoval)
                .build();


    private Scheduler createScheduler() {
        return forScheduledExecutorService(newSingleThreadScheduledExecutor());
    }

我是否会正确假设该onRemoval方法将在newWorkStealingPool(15)ForkJoinPool 上执行,并且调度程序只会被调用以查找需要被驱逐的过期条目?

这意味着它会像这样:

  1. 调用单线程调度程序(每 ~ 1 秒)
  2. 找到所有要驱逐的过期条目
  3. newWorkStealingPool(15)为缓存构建器中定义的每个逐出条目执行onRemoval ?

我没有找到解释这种行为的文档,所以我在这里问

肿瘤坏死因子

4

1 回答 1

2

您的假设很接近,只是它在实践中稍微优化了一些。

  1. 缓存读取和写入在底层哈希表上执行并附加到内部环形缓冲区。
  2. 当缓冲区达到阈值时,将提交任务Caffeine.executor以调用Cache.cleanUp
  3. 当此维护周期运行时(锁定),
    • 缓冲区被耗尽,事件根据驱逐策略重播(例如 LRU 重新排序)
    • 任何可驱逐的条目都将被丢弃,并提交一个任务Caffeine.executor以调用RemovalListener.onRemoval
    • 计算直到下一个条目到期的持续时间并将其提交给调度程序。这是由一个起搏器保护的,因此通过确保在计划任务之间发生约 1 秒来避免过度调度。
  4. 当调度程序运行时,一个任务被提交Caffeine.executor给调用Cache.cleanUp(参见#3)。

调度程序做最少的工作,任何处理都推迟到执行程序。由于使用 O(1) 算法,维护工作很便宜,因此它可能经常根据使用活动发生。它针对小批量工作进行了优化,因此计划调用之间强制执行的约 1 秒延迟有助于在每次调用时捕获更多工作。如果下一个过期事件发生在遥远的将来,那么调度程序将在此之前不会运行,尽管调用线程可能会由于它们在缓存上的活动而触发维护周期(参见 #1,2)。

于 2021-06-23T18:09:53.077 回答