在google guava loadingCache中添加一个值

时间:2014-10-29 04:18:34

标签: java multithreading caching concurrency guava

这是我的加载缓存定义:

  private class ProductValue {
      private long regionAValue;
      private long regionBValue;

      // constructor and general stuff here
  }

  private final LoadingCache<ProductId, ProductValue> productCache = CacheBuilder.newBuilder()
            .expireAfterAccess(4, TimeUnit.MINUTES)
            .build(new CacheLoader<ProductId, ProductValue>() {
                @Override
                public ProductValue load(final ProductId productId) throws Exception {
                    return updateProductValues(productId);
                }
            });

  private ProductValue updateProductValues(final ProductId productId) {
     // Read from disk and return
  }

现在,我是一个用例,我需要在缓存中设置regionA或regionB的值,直到下一次更新发生。我完全混淆了逻辑I&ve; ve的并发含义:

  public void setProductValue(final ProductId productId, final boolean isTypeA, final long newValue) throws ExecutionException {

    ProductValue existingValues = productCache.get(productId);   // 1
    if (isTypeA) {
        existingValues.regionAValue = newValue;
    } else {
        existingValues.regionBValue = newValue;
    }

    productCache.put(productId, existingValues);                  // 2
}

1中,我刚刚读取了存储在缓存中的给定密钥的信息的引用,这个get是线程安全的,因为加载缓存就像一个并发映射。但是在12之间,这个引用可以被其他一些线程覆盖。因为我已经覆盖了&#39;价值&#39;使用已存在于缓存中的引用,我是否需要将键值对放入缓存中?我需要第2行吗?

1 个答案:

答案 0 :(得分:2)

(免责声明:我不是番石榴缓存专家)

我认为您的代码中存在两个并发问题:

  1. 您有两个操作可以改变现有值中的对象,即existingValues.regionAValue = ...existingValues.setRegionValue(...)。当只应用一个操作时,其他线程可以看到状态。我认为这不是通缉。 (正确的吗?)
  2. get()put()之间,该值可能会再次加载到缓存中,put()会覆盖新值。
  3. 关于1:

    如果对对象有更多读取然后写入,一个好的选择是使用不可变对象。您不要触摸实例,而是执行原始对象的副本,变异,并将新对象放入缓存中。这样只有最终状态变得可见。

    关于2:

    Atomic CAS操作可以帮助您(例如JSR107兼容的缓存)。有用的方法是boolean replace(K key, V oldValue, V newValue);

    在Google Guava中,可以通过ConcurrentMap接口访问CAS方法,您可以通过asMap()检索它。