Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从Guava RemovalListener重新插入条目是否安全?_Java_Caching_Concurrency_Guava - Fatal编程技术网

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

Java 从Guava RemovalListener重新插入条目是否安全?,java,caching,concurrency,guava,Java,Caching,Concurrency,Guava,我有一个番石榴缓存(或者更确切地说,我正在从MapMaker迁移到Cache),这些值表示长期运行的作业。我想在缓存中添加expireAfterAccess行为,因为这是清理缓存的最佳方式;但是,作业可能仍在运行,即使它在一段时间内没有通过缓存访问,在这种情况下,我需要防止它从缓存中删除。我有三个问题: 在RemovalListener回调期间重新插入正在删除的缓存项是否安全 如果是这样的话,它是线程安全的吗?CacheLoader不可能在另一个线程中仍在进行RemovalListener回调时

我有一个番石榴
缓存
(或者更确切地说,我正在从
MapMaker
迁移到
Cache
),这些值表示长期运行的作业。我想在缓存中添加
expireAfterAccess
行为,因为这是清理缓存的最佳方式;但是,作业可能仍在运行,即使它在一段时间内没有通过缓存访问,在这种情况下,我需要防止它从缓存中删除。我有三个问题:

  • RemovalListener
    回调期间重新插入正在删除的缓存项是否安全

  • 如果是这样的话,它是线程安全的吗?
    CacheLoader
    不可能在另一个线程中仍在进行
    RemovalListener
    回调时为该键生成第二个值

  • 有没有更好的方法来实现我的目标?这不仅仅是一个“缓存”-每个键使用一个且只有一个值是至关重要的-但我还想在它所代表的作业完成后将条目缓存一段时间。我以前使用过
    MapMaker
    ,现在我需要的行为在该类中已被弃用。在作业运行时定期ping地图是不雅观的,在我的情况下是不可行的。也许正确的解决方案是有两个地图,一个没有逐出,另一个有,并在完成时将它们迁移到另一个地图上

  • 我还将提出一个功能请求——这将解决问题:允许锁定单个条目以防止逐出(然后解锁)


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

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


    请注意,正如Javadoc所述,
    softValues()
    的使用确实带来了折衷。

    我查看了Guava代码,发现
    CustomConcurrentHashMap
    (该
    CacheBuilder
    用作底层实现)将删除通知添加到内部队列,并在以后处理它们。因此,从移除回调将条目重新插入映射在技术上是“安全的”,但会打开一个窗口,在此期间该条目不在映射中,因此另一个线程可以通过
    CacheLoader
    生成备用条目


    我在上面的评论中使用@Louis的建议解决了这个问题。主映射根本没有过期,但每次我在该映射中查找某个内容时,我也会将一个条目添加到辅助缓存中,该缓存具有
    expireAfterAccess
    。在该辅助缓存的删除侦听器中,我决定是否从主映射中删除条目。没有其他东西使用辅助缓存。这似乎是一个优雅的有条件驱逐的解决方案,它的工作。(我仍然在使用Guava r09 MapMaker进行此解决方案,但它对Guava 11应该同样有效。)

    昨天升级到Guava 11时,我想到有一种方法可以将条目锁定到缓存中:使用
    maximumWeight
    作为唯一的删除方法,并让您的称重器为应该锁定在缓存中的条目返回0权重。我还没有通过测试来验证这一点,但是各州的文件

    “当条目的权重为零时,将不考虑基于大小的逐出(尽管仍可以通过其他方式逐出)。”


    为了直接解决文章中的前两个问题,从缓存中删除元素后,删除侦听器会在任意时间点触发,因此不能使用删除侦听器自动重新插入值

  • 在RemovalListener回调期间重新插入正在删除的缓存项是否安全
  • 是的,这是安全的;从
    RemovalListener
    中调用不会以任何方式中断缓存。广义地说,
    缓存

  • 如果是这样,它是线程安全的吗?这样,当RemovalListener回调仍在另一个线程中发生时,CacheLoader就不可能为该键生成第二个值了
  • 不,由
    RemovalListener
    触发的突变与相关的删除无关。在删除完成和侦听器被通知之间,不仅可能有另一个线程访问缓存,还可能在这两个操作之间存在明显的延迟。如前所述,“删除侦听器操作在缓存维护期间同步执行”。也就是说,维护任务是小批量执行的,对于何时(或是否)实际执行,没有提供任何保证

  • 有没有更好的方法来实现我的目标?这不仅仅是一个“缓存”