Using a thread safe static mutable Map of Map in Java

I need to add a static thread safe HashMap. I have something like this -

private static Map<String, ConcurrentHashMap<Integer, ClassA>> myCache =
      new ConcurrentHashMap<String, ConcurrentHashMap<Integer, ClassA>>();

Even though I am using ConcurrentHashMap, I see that if main thread is adding element in the myCache, and when the same is accessed in other thread even at later time, it does not have the latest data in the myCache object. For e.g

Thread 1: Adds entry to the map myCache = {a1={1=com.x.y.z.ClassA@3ec8b657}}

Thread 2: Access the same key a1. But it does not see the data added by Thread 1. Rather it sees empty value for this key myCache = {a1={}} As a result, data is getting corrupted. Entries added for the a1 key in Thread 1 are not visible in Thread 2.

Thanks in advance for any pointers on how can I update this map in thread safe manner.


Solution 1:

Even though I am using ConcurrentHashMap, I see that if main thread is adding element in the myCache, and when the same is accessed in other thread even at later time, it does not have the latest data in the myCache object.

ConcurrentHashMap is running in a large number of applications around the world at this instance. If your fairly typical use case didn't work appropriately, many critical systems would be failing.

Something is going on but chances are high that it is nothing to do with ConcurrentHashMap. So here are some questions for you to help you debug your code:

  • Are you sure that the cache lookup happens after the cache put? ConcurrentHashMap doesn't save you from race conditions in your code.
  • Any chance this is a problem with the hashcode() or equals() functions of the key object? By default the hashcode() and equals() methods are for the object instance and not the object value. See the consistency requirements.
  • Any chance that the cache lookup happens after a cache remove or a timeout of the cache value? Is there cache cleanup logic?
  • Do you have logic that does 2 operations on the ConcurrentHashMap. For example testing for existence of a cache entry and then making another call to put the value? You should be using putIfAbsent(...) or the other atomic calls if so.

If you edit your post and show a small sample of your code with the key object, the real source of the issue may be revealed.

Solution 2:

The @Ryan answer is essentially correct.

Remember that you must proxy every Map method that you wish to use and you must synchronize to the cashe element within every proxied method.

For example:

public void clear()
{
  synchronized(cache)
  {
    cache.clear();
  }
}

Solution 3:

I've never had good results with ConcurrentHashMap. When I need thread safety, I usally do something like this:

public class Cache<K, V> {
private Map<K, V> cache = new HashMap<>();

public V get(K key) {
    synchronized (cache) {
        return cache.get(key);
    }
}

public void put(K key, V value) {
    synchronized(cache) {
        cache.put(key, value);
    }
}
}