【问题标题】:Scenario: Why to use ConcurrentHashMap?场景:为什么要使用 ConcurrentHashMap?
【发布时间】:2017-08-31 08:26:28
【问题描述】:

最近我参与了讨论,其中给了我一个需要实现以支持以下操作的缓存:

int getData(String key){
     if(cache.get(key) !=null){
             return cache.get(key);
     } else {
         int val = getValueFromDB();  // line no. 5
         cache.put(key, val);        // line no. 6
         return val;
     }

现在的问题是,在多线程场景中,要实现缓存,你会使用哪个:HashMap 还是 ConcurrentHashMap?

我的回答:ConcurrentHashMap,因为它允许在相同和不同的段上进行读取操作,并且只允许在不同的段上进行写入操作。

lead 的论点是,由于 key 相同,多个线程将执行到line 5,但只有一个线程会执行 `line no. 6. 由于是DB调用,如果不存在,只需执行一次即可将值放入缓存。

我:我可以让getData同步。

Lead:那为什么是ConcurrentHashMap?普通的 HashMap 也可以。

我:我会将line no. 5line no. 6 放在同步块中。

Lead:然后多个线程将在块处等待。当一个线程执行并通知其他线程时,下一个线程将执行 db 调用。

现在,我们如何才能做到这一点?基本上,我们不想执行多个数据库调用。它应该由一个线程一次调用完成。

请指教。

【问题讨论】:

  • 我不明白为什么“密钥相同”。不同的线程肯定会得到不同的键吗?在那个代码中也不是这样,“只有一个人会执行第 1 行。 6'。访问相同键的两个线程肯定找不到条目并开始访问数据库。事实上,如果数据库访问需要一段时间,最终可能会导致线程队列获取相同的密钥。将它放在synchronized block 中将完全违背使用 ConcurrentHashMap 使写入操作顺序的目的。 computeIfAbsent() 下面的答案解决了这个问题。
  • 当然如果使用HashMap 也需要synchronized 读取(或实现ReadWriteLock)。
  • 知道了。不知道computeIfAbsent() 方法。它是 Java 8 的一部分吗?
  • 是的 Java 1.8,以前不存在,因为使用 lambda 表达式。请注意在锁定方法中注入代码的锁定后果。具体来说,在访问数据库时将阻止对该缓存条的所有访问。一个高可扩展性的解决方案会添加一个占位符,然后单个线程将在添加结果的条件下挂起。可能是过度工程。

标签: java multithreading hashmap concurrenthashmap


【解决方案1】:

他在几乎所有方面都是正确的。您遇到的问题是您没有充分利用 ConcurrentHashMap 提供的功能。

您正在使用 Map - get()put() 的常规方法 - 这会导致 "check-then-act"

不用说,这个问题已经解决了:有一个computeIfAbsent 方法几乎可以满足您的需要:

int getData(String key){
     return cache.computeIfAbsent(key, k -> getValueFromDB());
}

我最初建议使用putIfAbsent,但这样做的问题是getValueFromDB 函数将被评估,无论它是否需要。

【讨论】:

    【解决方案2】:

    这里的答案是使用ConcurrentHashMapcomputeIfAbsent()。实现此方法是为了从 Map 中获取键的值,如果不存在,则根据提供的映射 Function 进行计算。在ConcurrentHashMap 上,它将自动执行此操作。

    因此,在您的情况下,将实现该函数以从数据库中获取条目。由于这种情况以原子方式发生,因此您可以保证它只发生一次。

    像这样:

    int getData(String key){
        return cache.computeIfAbsent(key, k -> getValueFromDb());
    }
    

    【讨论】:

      猜你喜欢
      • 2013-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 2017-08-24
      • 1970-01-01
      • 2014-07-10
      相关资源
      最近更新 更多