如何以原子方式更新多线程应用程序中的ConcurrentMap值?

时间:2016-12-14 06:17:04

标签: java multithreading thread-safety concurrenthashmap atomicity

我需要从多线程应用程序填充ConcurrentMap。我的地图如下所示:

  private final ConcurrentMap<String, AtomicLongMap<String>> deviceErrorHolder = Maps.newConcurrentMap();

下面是我的方法,它以非常快的速度从多线程应用程序调用,所以我需要确保它很快。

  public void addDeviceErrorStats(String deviceName, String errorName) {
    AtomicLongMap<String> errorMap = deviceErrorHolder.get(deviceName);
    if (errorMap == null) {
      errorMap = AtomicLongMap.create();
      AtomicLongMap<String> currenttErrorMap = deviceErrorHolder.putIfAbsent(deviceName, errorMap);
      if (currenttErrorMap != null) {
        errorMap = currenttErrorMap;
      }
    }
    errorMap.incrementAndGet(errorName);
  }

对于每个deviceName,我会有AtomicLongMap,其中包含不同errorName的所有计数。

  ExceptionCounter.getInstance().addDeviceErrorStats("deviceA", "errorA");
  ExceptionCounter.getInstance().addDeviceErrorStats("deviceA", "errorB");
  ExceptionCounter.getInstance().addDeviceErrorStats("deviceA", "errorC");

  ExceptionCounter.getInstance().addDeviceErrorStats("deviceB", "errorA");
  ExceptionCounter.getInstance().addDeviceErrorStats("deviceB", "errorB");

我的addDeviceErrorStats方法线程是否安全?而且我更新deviceErrorHolder地图的价值的方式是正确的吗?意思是原子操作吗?我是否需要同步创建新的AtomicLongMap实例?或者CM会照顾我吗?

我正在使用Java7。

2 个答案:

答案 0 :(得分:1)

您可以使用computeIfAbsent()创建 lot 更简单的版本。

AtomicLongMap<String> errorMap = deviceErrorHolder.computeIfAbsent(deviceName, a -> AtomicLongMap.create());
errorMap.incrementAndGet(errorName);

computeIfAbsent(在并发映射中)特别适用于执行空检查逻辑的原子版本。如果deviceName键有值,则返回它,否则计算将以原子方式调用,并且计算的返回值既与地图中的键相关联又返回。

答案 1 :(得分:0)

我相信你的方法是正确的。假设我们有两个并发线程为同一个设备调用它

errorMap已经存在的情况很简单,因为两个线程都会相同并在其上调用incrementAndGet,这是原子的。

现在让我们考虑一下errorMap不存在的情况。假设第一个线程到达AtomicLongMap.create(),然后安排第二个线程。这样的线程也会创建自己的本地地图。 putIfAbsent()是原子的,因此其中一个线程将返回null,而第二个将返回第一个放置的地图。在后一种情况下,您将丢弃由此线程实例化的地图,并使用返回的地图。对我来说很好看。