为什么Cache.asMap()与Cache.size()不一致?

卢声远*_* Lu 7 java concurrency caching guava

在番石榴图书馆,我很困惑为什么Cache.asMap()不符合Cache.size(),除非Cache.cleanUp()被称为.

Cache<Object, Object> cache = CacheBuilder.newBuilder()
           .expireAfterWrite(1, TimeUnit.SECONDS)
           .build();
cache.get(...);
...
//After some seconds, all entries are expired.
//cache.asMap() is EMPTY Map, but cache.size() != 0
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:这Cache.asMap()是不一致的错误Cache.size()吗?虽然我注意到javadoc Cache.size()是:

  /**
   * Returns the **approximate** number of entries in this cache.
   */
Run Code Online (Sandbox Code Playgroud)

我猜它可能与并发环境有关.那到底Cache.cleanUp()做了什么?

Ben*_*nes 14

Guava的缓存是围绕锁定摊销设计的,该cleanUp方法强制缓存达到一致状态.该Map.size()方法是近似值,但可以计算由于到期或参考驱逐而等待删除的条目.Guava高速缓存中近似值的可见性对于应用程序来说很少有用,它往往将高速缓存视为瞬态数据存储.来自Map的缓存的不同期望导致asMap允许将缓存视为地图的方法,但不利于开发人员以这种方式感知它.

StrangleLoop 2011会议幻灯片介绍了缓存的实现细节.在设计文件ConcurrentLinkedHashMap,其中番石榴的缓存是来源于,也可能是利益,而是描述了一个稍微不同的方法.


小智 12

Ben给出了很好的高层回应.低级响应是:

asMap()视图可以遍历缓存中的每个元素,因此可以跳过待清除的无效条目.另一方面,size()预计将是一个快速操作,并且仅仅为了获得更准确的大小估计而遍历整个缓存将是愚蠢的.

CacheBuilder的javadoc进入更详细关于需要在各种情况下发生的(如清理expireAfterWrite,你的情况).