I’m trying to make a thread safe singleton cache of ConcurrentHashMaps using a Google Guava Cache. Each of these maps will contain a List. The List will only be read ONCE after all threads which could add to it have executed. I’m wondering if my implementation (specifically where I update an item) is thread safe/how to improve it. Is there a better way to do this without using a synchronized block?
public enum MyCache {
INSTANCE;
private static Cache<Integer, ConcurrentHashMap<String, List>> cache =
CacheBuilder.newBuilder()
.maximumSize(1000)
.build();
private static AtomicInteger uniqueCount = new AtomicInteger(0);
private final Object mutex = new Object();
//Create a new unique ConcurrentHashMap
public Integer newMapItem(){
Integer key = uniqueCount.incrementAndGet();
//We dont care if something exists
cache.put(
key,
new ConcurrentHashMap<String, List>()
);
return key;
}
public void expireMapItem(int key){
cache.invalidate(key);
}
public Integer add(int cacheKey, String mapListKey, int value){
synchronized(mutex){
ConcurrentMap<String, List> cachedMap = cache.getIfPresent(cacheKey);
if (cachedMap == null){
//We DONT want to create a new map automatically if it doesnt exist
return null;
}
List mappedList = cachedMap.get(mapListKey);
if(mappedList == null){
List newMappedList = new List();
mappedList = cachedMap.putIfAbsent(mapListKey, newMappedList);
if(mappedList == null){
mappedList = newMappedList;
}
}
mappedList.add(value);
cachedMap.replace(mapListKey, mappedList);
cache.put(
cacheKey,
cachedMap
);
}
return value;
}
}
If multiple threads can write to a given
List(which should be aList<Integer>, since you are addingints to it), you’ll need to synchronize on something. However, you don’t need that global lock. Moreover, you seem to think thatCacheandConcurrentHashMapcopy the objects you put into them and get from them, since you’re putting them again once they have been updated, but they don’t: they hold references to what you put into them.I’d change the
add()method this way:Actually, I’d create a
CacheofLoadingCaches, instead of aCacheofConcurrentHashMap, it makes the code inadd()more simple, moving the creation of theListto theCacheLoaderimplementation. You can still expose theLoadingCaches asMaps using theasMap()method. I’ve removed some boxing/unboxing as well.EDIT: changed the return type of
add()tobooleaninstead ofintwhich doesn’t work with the originalreturn null(when the return type wasInteger). No need for a potential NPE.