提问者:小点点

场景:为什么要使用并发HashMap?


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

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?

我的ans:并发HashMap,因为它允许对相同和不同的段进行读操作,并且只允许对不同的段进行写操作。

引线的论点是,由于键是相同的,多个线程将执行第5行,但只有一个线程将执行第6行。由于这是一个DB调用,如果不存在,它只需要执行一次即可将值放在缓存中。

我:我可以让getData同步。

导语:那为什么是并发HashMap?普通HashMap也可以。

我:我会把行。5行。6在同步块内。

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

现在,我们如何实现这一点?基本上,我们不想执行多个db调用。它应该由一个线程完成,只需一次调用。

请指教。


共2个答案

匿名用户

这里的答案是使用ConCurrentHashMapcomputeIfAbsend()。实现此方法是为了从Map中获取键的值,如果不存在,则在给定提供的映射Function的情况下计算它。在ConCurrentHashMap上,它将以原子方式执行此操作。

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

像这样:

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

匿名用户

他几乎在每件事上都是对的。你遇到的问题是你没有充分利用并发HashMap提供的功能。

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

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

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

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