How to atomically update a ConcurrentMap value in a multi-threaded application?

I have a ConcurrentMap that I need to populate from a multi-threaded application. My map is shown below:

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

Below is my method, which is called from a multi-threaded application with a very high speed, so I need to make sure it is fast.

  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); } 

For each deviceName , I will have AtomicLongMap , which will contain all the calculations for different 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"); 

Is addDeviceErrorStats thread addDeviceErrorStats safe? And also the way I update the value of my deviceErrorHolder map, right? Will it be an atomic operation? Do I need to synchronize the creation of new AtomicLongMap instances? Or will CM take care of me?

I am working with Java7.

0
source share
2 answers

You can create a much simpler version using computeIfAbsent() .

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

computeIfAbsent (in parallel maps) is specifically designed to do an atomic version of what your logic of checking for zeros does. If the deviceName key has a value, it is returned, otherwise the calculation is called atomic, and the return value of the calculation is associated with the key on the map, and is also returned.

+1
source

I believe your method is correct. Suppose we have two parallel threads invoking it for the same device.

The case where the error already exists is trivial, since both threads will get the same and call incrementAndGet on it, which is atomic.

Now consider the case where errorMap does not exist. let's say that the first thread goes to AtomicLongMap.create() , and then the second thread goes. Such a stream will also create its own local map. putIfAbsent() is atomic, so one of the threads will return null, and the second will return the card put by the first. In the latter case, you discard the card created by this thread and using the one that was returned. It looks good to me.

0
source

All Articles