【问题标题】:ConcurrentHashMap vs ReentrantReadWriteLock based Custom Map for ReloadingConcurrentHashMap 与基于 ReentrantReadWriteLock 的自定义地图重新加载
【发布时间】:2012-08-24 12:11:13
【问题描述】:

Java 大师,

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

要解决这个问题,我有以下选择:

A.使用ConcurrentHashMap

这看起来像是第一选择,但我们正在谈论的操作是reload() - 表示clear(),然后是replaceAll()。因此,如果在clear()replaceAll() 之前读取Map,它返回null,这是不可取的。即使我synchronize 这也不能解决问题。

B.基于ReentrantReadWriteLock

创建另一个实现

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

最好的出路是什么?

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

【问题讨论】:

  • 您的地图实际上是不可变的,这使它们成为通过 volatile 发布的完美目标。
  • 实际上是不可变的对不起,我不太明白immutable...你能提供一些提示吗???

标签: java locking concurrenthashmap reentrantreadwritelock


【解决方案1】:

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

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

【讨论】:

  • 您能详细解释一下吗?我很难解释为什么这会奏效。我确实知道 volatile 会保证可见性,但是像 clear 而不是 replaceAll 这样的复合操作将如何工作?谢谢。
  • 要替换整个地图,您所要做的就是在构建新副本时将新地图分配给您字段中的旧易失性引用。也就是说,您真的要替换所有内容,甚至是地图本身,而不仅仅是其内容。
【解决方案2】:

您似乎不确定如何实施 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,保证其他线程会看到变化

【讨论】:

    【解决方案3】:

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

    真正的问题是,在输入 String 的情况下,您是否可以指定如何计算您的 SomeApplicationObject。仅根据您到目前为止告诉我们的内容,它可能看起来像这样......

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

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

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

    【讨论】:

    • SomeApplicationObject 是一个实体,它在应用程序启动时从 DB 中预加载并一直从 Map 中读取...有时系统管理员可以重新加载这些值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多