【问题标题】:Spring @Cacheable double-checked locking (infinispan backend)Spring @Cacheable 双重检查锁定(infinispan 后端)
【发布时间】:2017-07-03 08:28:03
【问题描述】:

我最近开始在应用程序中使用 Spring 的 @Cacheable 注释来缓存一些昂贵操作的结果。我要替换的代码是广泛使用双重检查锁定模式来确保缓存的一致性和昂贵的例程只执行一次,如下所示:

final String key = SomeClass.computeKey(input)
String cachedValue = mCache.get(key);
if (cachedValue == null) {
    synchronized (mCache) {
        cachedValue = mCache.get(key);
        if (cachedValue == null) {
            cachedValue = expensiveComputing(input);  // must not be executed twice
            mCache.put(key, cachedValue);
        }
    }
}

上面被替换为:

@Cacheable(value="someCache", key="T(SomeClass).computeKey(#input)")
public String expensiveComputing(Object input) {
    // must not be executed twice
} 

有时会有第二个缓存来缓存锁定对象以进行实际查找,以避免在计算缓存值期间对整个缓存进行大量同步。

我已经阅读了infinispan's docu on locking,但我仍然想知道我是否需要自己处理这些方面,或者 infinispan 是否可以保证预期的行为。

根据文档 infinispan,从 5.0 开始默认为每个缓存条目创建一个新锁(我使用的是 8.2.6)。那就是锁对象和缓存值的2级缓存应该是不必要的,如果我得到这个正确?!但是双重检查锁定模式呢?这个也可以去掉吗?在高度并发的环境中,expensiveComputing 例程是否仍然只被调用一次?

【问题讨论】:

  • 应该不需要那些 synchronized 部分。 Infinispan Cache 包含 put if 如果不存在或类似比较和交换的操作,可提供您所需的保证。更准确地说,上面的代码可以只替换为mCache.putIfAbsent。我不是专家,但我不知道 Spring @Cacheable 注释的等价物是什么。我的一位同事或许可以提供帮助。
  • 这是正确的方法。请使用putIfAbsent
  • @GalderZamarreño,@altanis,不完全是。我需要一个同步的computeIfAbsent,上面提到的sync 标志承诺提供。

标签: java spring caching infinispan


【解决方案1】:

简答

Spring 4.3+ 和 Infinispan 9.0+ 支持 @Cacheable 中的 sync=true(请参阅 this Spring ticketthis Infinispan ticket

长答案

Pre-Spring 4.3,简单地用@Cacheable 注释方法不会同步代码本身。它所做的只是将方法包装到一个代理中(有关详细信息,请参阅o.s.c.a.JCacheCacheAspecto.s.c.j.i.JCacheAspectSupport),当它在调用方法体之前尝试从缓存中获取值时,它不包括任何额外的同步。

因此,昂贵的计算完全有可能发生两次。在这种情况下,额外的同步取决于程序员。所以双重检查锁还是有用的。

还有另一种选择:由于 Infinispan 将其缓存库作为 JCache (JSR-107) 提供程序提供,因此您可以考虑编写自己的 org.infinispan.jcache.annotation.AbstractCacheResultInterceptor 实现,而不是使用默认的 org.infinispan.jcache.annotation.InjectedCacheResultInterceptor,这样会小心的锁定。例如,您的类实现可能有一些字典来决定哪些类型的InvocationContext 需要这种锁定。

实际上,在这种情况下使用@Cacheable 似乎带来的痛苦多于收获。从事务管理器创建缓存并使用它来像在 Infinispan tutorials 中那样对事务进行一些控制似乎更容易、更简洁。

从 Spring 4.3 开始,@Cacheable 有一个 sync 属性,暗示整个操作必须同步。 Infinispan 从 9.0 版开始支持此功能。

【讨论】:

  • 不幸的是,ISPN-7224 几分钟前已重新打开。因此 9.0 中的修复似乎并不完整:-(
  • @dpr - 事实证明,对于客户端/服务器模式,我们需要额外的同步(目前实现为轻量级客户端锁定)。不过嵌入式场景应该没问题。
  • @altanis,感谢您提供的信息。不幸的是,当前无法更新到 infinispan 9.0,因为我们正在使用异步传输选项,该选项在 infinispan 9 中减少了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-08
  • 1970-01-01
相关资源
最近更新 更多