10

爪哇大师,

目前我们有一个经常HashMap<String,SomeApplicationObject>读取和偶尔修改的文件,我们遇到了在修改/重新加载期间,读取操作返回的问题,null这是不可接受的。

为了解决这个问题,我有以下选项:

A. 使用ConcurrentHashMap

这看起来像是第一个选择,但我们正在谈论的操作是reload()- 意味着clear()后跟replaceAll()。因此,如果Map阅读 postclear()和 prereplaceAll()它返回 null 这是不可取的。即使我synchronize这并不能解决问题。

B. 基于 ReentrantReadWriteLock创建另一个实现

我将在操作Write Lock前创建获取reload()。这似乎更合适,但我觉得必须有一些可用的东西,我不需要重新发明轮子。

最好的出路是什么?

编辑是否已经有任何具有此类功能的收藏?

4

3 回答 3

8

由于您正在重新加载地图,我会在重新加载时替换它。

您可以使用 volatile Map 来做到这一点,当它更新时您会完全替换它。

于 2012-08-24T12:13:14.250 回答
5

您似乎不确定如何实施 Peter Lawrey 的建议。它可能看起来像这样:

class YourClass {
    private volatile Map<String, SomeApplicationObject> map;

    //constructors etc.

    public void reload() {
        Map<String,SomeApplicationObject> newMap = getNewValues();
        map = Collections.unmodifiableMap(newMap);
    }
}

没有并发问题,因为:

  • 新地图是通过局部变量创建的,根据定义,该变量不共享 -getNewValues不需要同步或原子
  • 分配给map是原子的
  • map是 volatile 的,保证其他线程会看到变化
于 2012-08-29T09:04:59.860 回答
5

这听起来很像Guava 的Cache尽管它实际上取决于您如何填充地图以及如何计算值。(披露:我为 Guava 做出了贡献。)

真正的问题是您是否可以指定如何计算SomeApplicationObject给定的输入String。就目前为止你告诉我们的情况来看,它可能看起来像这样......

LoadingCache<String, SomeApplicationObject> cache = CacheBuilder.newBuilder()
   .build(
       new CacheLoader<String, SomeApplicationObject>() {
         public SomeApplicationObject load(String key) throws AnyException {
           return computeSomeApplicationObject(key);
         }
       });

然后,每当您想重建缓存时,您只需调用cache.invalidateAll(). 使用 a LoadingCache,您可以调用cache.get(key),如果它尚未计算该值,它将被重新计算。或者也许在调用之后cache.invalidateAll(),您可以调用cache.loadAll(allKeys),尽管您仍然需要能够一次加载单个元素,以防在invalidateAlland之间出现任何查询loadAll

如果这是不可接受的——如果你不能单独加载一个值,你必须一次加载它们——那么我会继续使用 Peter Lawrey 的方法——保留volatile对地图的引用(理想情况下是一个ImmutableMap) ,重新计算整个地图并在完成后将新地图分配给参考。

于 2012-08-24T16:21:49.227 回答