java - 如何从多个线程填充并发 HashMap ?

标签 java multithreading thread-safety concurrenthashmap

我有一个 ConcurrentHashMap,我从多个线程填充它。

private static Map<DataCode, Long> errorMap = new ConcurrentHashMap<DataCode, Long>();

public static void addError(DataCode error) {
    if (errorMap.keySet().contains(error)) {
        errorMap.put(error, errorMap.get(error) + 1);
    } else {
        errorMap.put(error, 1L);
    }
}

我上面的addError方法是从填充errorMap的多个线程调用的。我不确定这是否是线程安全的?我在这里做错了什么吗?

任何关于为什么它可以跳过更新的解释都将帮助我更好地理解。

最佳答案

这是否安全取决于您的意思。它不会抛出异常或损坏 map ,但它可以跳过更新。考虑:

  1. 线程 1:errorMap.get(error) 返回 1
  2. 线程2:errorMap.get(error)返回1
  3. 线程1:errorMap.put(error, 1+1);
  4. 线程2:errorMap.put(error, 1+1);

围绕 keySet().contains(error) 操作存在类似的竞争。要解决此问题,您需要使用原子操作来更新 map 。

在 Java 8 上,这很简单:

errorMap.compute(error, oldValue -> oldValue == null ? 1L : oldValue + 1L);

在旧版本的 Java 上,您需要使用比较和更新循环:

Long prevValue;
boolean done;
do {
  prevValue = errorMap.get(error);
  if (prevValue == null) {
    done = errorMap.putIfAbsent(error, 1L);
  } else {
    done = errorMap.replace(error, prevValue, newValue);
  }
} while (!done);

使用此代码,如果两个线程竞争,一个线程可能最终会重试其更新,但它们最终会获得正确的值。

或者,您也可以使用 Guava 的 AtomicLongMap它为您完成所有线程安全魔法并获得更高的性能(通过避免所有这些装箱操作等):

errorAtomicLongMap.incrementAndGet(error);

关于java - 如何从多个线程填充并发 HashMap ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30928994/

相关文章:

java - 处理流数据、更新 GUI、使用线程,并且不错过任何一个节拍?

javascript - 从回调 : synchronization issues? 填充本地数组

java - 从单独的线程添加到 ArrayList

java - 确定矩阵是否是魔法矩阵

java - 无法在 Servlet 中解析参数

java - 在 React Native Android 应用程序中通过 crashlytics 的堆栈跟踪找到崩溃是真的吗?

.net - Lambda多线作为线程参数?

java - 并发链接队列使用CAS

java - 当有多个线程时,跳出循环的最佳方法是什么?

java - Gate - 从注释集中提取单独的注释文本