从Guava RemovalListener重新插入条目是否安全?

Dav*_*oha 13 java concurrency caching guava

我有一个番石榴Cache(或者说,我从迁移MapMakerCache)和值代表长时间运行的作业.我想将expireAfterAccess行为添加到缓存中,因为这是清理缓存的最佳方法; 但是,即使在一段时间内没有通过缓存访问该作业,该作业仍可能仍在运行,在这种情况下,我需要阻止它从缓存中删除.我有三个问题:

  1. 重新插入在RemovalListener回调期间被删除的缓存条目是否安全?

  2. 如果是这样,它是线程安全的,这样,CacheLoaderRemovalListener回调仍在另一个线程中发生时,没有可能产生该键的第二个值吗?

  3. 有没有更好的方法来实现我想要的?这不是严格/只是一个"缓存" - 每个密钥只使用一个且只有一个值是至关重要的 - 但我也希望在它所代表的工作完成后将条目缓存一段时间.我以前使用MapMaker过,我需要的行为现在在该类中被弃用了.在作业运行时定期ping地图是不优雅的,在我的情况下,不可行.也许正确的解决方案是拥有两个地图,一个没有驱逐,一个有,并在完成时将它们迁移.

我也会提出一个功能请求 - 这可以解决问题:允许锁定单个条目以防止驱逐(然后解锁).

[编辑以添加一些细节]:此地图中的键指的是数据文件.这些值可以是正在运行的写入作业,也可以是已完成的写入作业,或者 - 如果没有正在运行的作业 - 是一个只读,生成在查找对象,其中包含从文件中读取的信息.重要的是每个文件正好有零个或一个条目.我可以为这两件事使用单独的地图,但必须在每个键的基础上进行协调,以确保一次只存在一个或另一个.在获得并发性方面,使用单个映射使其更简单.

小智 2

我不完全清楚确切的问题,但另一种解决方案是使用缓存softValues()而不是最大大小或过期时间。每次访问缓存值(在您的示例中,开始计算)时,您应该在其他地方维护对此值的强引用的状态。这将防止该值被GC。每当该值的使用量降至零时(在您的示例中,计算结束并且该值可以消失),您可以删除所有强引用。例如,您可以使用AtomicLongMap并将 Cache 值作为 AtomicLongMap 键并定期调用removeAllZeros()映射。

请注意,正如 Javadoc 所说,使用softValues()确实需要权衡。