ConcurrentHashMap 在增加其值时是否需要同步? [英] Does ConcurrentHashMap need synchronization when incrementing its values?

查看:58
本文介绍了ConcurrentHashMap 在增加其值时是否需要同步?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道 ConcurrentHashMap 是线程安全的,例如 putIfAbsent、Replace 等,但我想知道,像下面这样的代码块是否安全?

I know ConcurrentHashMap is thread-safe e.g.putIfAbsent,Replace etc., but I was wondering, is a block of code like the one below safe?

if (accumulator.containsKey(key)) { //accumulator is a ConcurrentHashMap
    accumulator.put(key, accumulator.get(key)+1); 
} else {
    accumulator.put(key, 0); 
}

请记住,一个键的累加器值可能会同时被两个不同的线程询问,这会导致普通 HashMap 出现问题.所以我需要这样的东西吗?

Keep in mind that the accumulator value for a key may be asked by two different threads simultaneously, which would cause a problem in a normal HashMap. So do I need something like this?

ConcurrentHashMap<Integer,Object> locks;
...
locks.putIfAbsent(key,new Object());
synchronized(locks.get(key)) {
    if (accumulator.containsKey(key)) { 
        accumulator.put(key, accumulator.get(key)+1); 
    } else {
        accumulator.put(key, 0); 
    }
}

推荐答案

if (accumulator.containsKey(key)) { //accumulator is a ConcurrentHashMap
    accumulator.put(key, accumulator.get(key)+1); 
} else {
    accumulator.put(key, 0); 
}

不,这段代码不是线程安全的;accumulator.get(key) 可以在 getput 之间更改,也可以在 containsKey 之间添加条目put.如果您使用的是 Java 8,则可以编写 accumulator.compute(key, (k, v) -> (v == null) ? 0 : v + 1),或任何许多等价物,它会起作用.如果你不是,要做的就是写一些像

No, this code is not thread-safe; accumulator.get(key) can be changed in between the get and the put, or the entry can be added between the containsKey and the put. If you're in Java 8, you can write accumulator.compute(key, (k, v) -> (v == null) ? 0 : v + 1), or any of the many equivalents, and it'll work. If you're not, the thing to do is write something like

while (true) {
  Integer old = accumulator.get(key);
  if (old == null) {
    if (accumulator.putIfAbsent(key, 0) == null) {
      // note: it's a little surprising that you want to put 0 in this case,
      // are you sure you don't mean 1?
      break;
    }
  } else if (accumulator.replace(key, old, old + 1)) {
    break;
  }
}

...循环直到它设法进行原子交换.这种循环几乎就是你必须如何做的:它是 AtomicInteger 的工作方式,你要求的是 AtomicInteger许多键.

...which loops until it manages to make the atomic swap. This sort of loop is pretty much how you have to do it: it's how AtomicInteger works, and what you're asking for is AtomicInteger across many keys.

或者,您可以使用库:例如Guava 有 AtomicLongMapConcurrentHashMultiset,它们也做类似的事情.

Alternately, you can use a library: e.g. Guava has AtomicLongMap and ConcurrentHashMultiset, which also do things like this.

这篇关于ConcurrentHashMap 在增加其值时是否需要同步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆