Search code examples
javaconcurrencygsonconcurrenthashmap

How to implement concurrent clear and copy map for ConcurrentHashMap


In my project I built Jersey Web Service. This service has scheduled task that runs every minute and fills up (by putIfAbsent function) the concurrent hash map (but clears it before). Additionally the web service has endpoint that returns json of the map:

return gson.toJson(map, type)

By JAVA API function clear isn't a concurrent function, and gson.toJson uses Map API functions only, so I need concurrent clear and getMap/copyMap functions. So I thought to implement these functions by myself with concurrent functions usage:

public class ExtendedConcurrentHashMap<K,V> extends ConcurrentHashMap<K,V> {
  private final ConcurrentHashMap<K,V> map;

  public ExtendedConcurrentHashMap(ConcurrentHashMap<K,V> map) {
    this.map = map;
  }

  public void clear() {
    ConcurrentHashMap.KeySetView<K,V> keySet = map.keySet(); //concurrent function
    Iterator<K> iterator = keySet.iterator();

    while(iterator.hasNext()) {
      K keyToRemove = iterator.next();
      map.compute(keyToRemove, (key,value) -> null); // concurrent function
    }
  }

  public Map<K,V> getMap() {
    Map<K,V> mapCopy = new HashMap<>();
    map.forEach((key,value) -> mapCopy.put(key,value)); // concurrent function

    return mapCopy;
  }
}

Is it the right way to do it? I don't want to use read/write locks or synchronized functions, because get json from endpoint performance is very important for my web service.

Partially populated map does not disturb me. Get performance and not to get exceptions during read/write/update are the most important for me


Solution

  • The implementation of Map.clear() in ConcurrentHashMap is thread safe. The comments in the documentation refer to the consistency of the data when viewed from different threads:

    For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries.

    Your solution suffers from the same problem, and the approach can't be made to work as there is no global lock in ConcurrentHashMap (indeed, reads never lock).

    If you want to perform bulk operations atomically you will have to do your locking manually. Collections.synchronizedMap() provides a wrapper that makes this simple. The wrapper itself is used as the lock for all operations, so synchronising on it allows multiple operations to be performed without fear that other threads will perform updates or reads before the work completes.