【发布时间】: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. 5 和line no. 6 放在同步块中。
Lead:然后多个线程将在块处等待。当一个线程执行并通知其他线程时,下一个线程将执行 db 调用。
现在,我们如何才能做到这一点?基本上,我们不想执行多个数据库调用。它应该由一个线程一次调用完成。
请指教。
【问题讨论】:
-
我不明白为什么“密钥相同”。不同的线程肯定会得到不同的键吗?在那个代码中也不是这样,“只有一个人会执行第 1 行。 6'。访问相同键的两个线程肯定找不到条目并开始访问数据库。事实上,如果数据库访问需要一段时间,最终可能会导致线程队列获取相同的密钥。将它放在
synchronized block中将完全违背使用 ConcurrentHashMap 使写入操作顺序的目的。computeIfAbsent()下面的答案解决了这个问题。 -
当然如果使用
HashMap也需要synchronized读取(或实现ReadWriteLock)。 -
知道了。不知道
computeIfAbsent()方法。它是 Java 8 的一部分吗? -
是的 Java 1.8,以前不存在,因为使用 lambda 表达式。请注意在锁定方法中注入代码的锁定后果。具体来说,在访问数据库时将阻止对该缓存条的所有访问。一个高可扩展性的解决方案会添加一个占位符,然后单个线程将在添加结果的条件下挂起。可能是过度工程。
标签: java multithreading hashmap concurrenthashmap