I am using a Thread (Let's call it "MapChecker") which is looping during its entire lifetime on a ConcurrentHashMap.
The map is populated from other threads and its cleared by the MapChecker by using iterators over it.
The map has the following structure:
private volatile Map<MyObject, SynchronizedList<MyOtherObject>> map = new ConcurrentHashMap<>();
//SynchronizedList = Collections.syncrhonizedList.
MapChecker must update the values of each key inside its loop. Updates are made by removing elements from the lists or removing the full map entry.
Synchronization occurs on two steps:
- When adding data inside the map (synchronized)
- When retrieving the iterator of the map (synchronized inside MapChecker).
The locks are on the mapitself ( synchronized(map) ).
I don't care to have always the last updated values inside my iterator view, but i need to be sure that all the missing values will be retrieved in the next iterations, this is important because I do not want to skip any element. Furthermore, it's important to have also the SynchronizedList correctly updated.
My question is: Can i be sure to get all the entries inserted / updated by having this kind of architecture? Is there the risk to miss something? What happens if MapChecker deletes an entry while an other thread is updating the same entry? ConcurrentHashMap should block these operation so i'm expecting no troubles.
This is the MapChecker loop:
while (!isInterrupted()) {
executeClearingPhases();
Iterator<Map.Entry<PoolManager, List<PooledObject>>> it = null;
synchronized (idleInstancesMap) {
it = idleInstancesMap.entrySet().iterator();
}
while (it.hasNext()) {
Map.Entry<PoolManager, List<PooledObject>> entry = it.next();
PoolManager poolManager = entry.getKey();
boolean stop = false;
while (!stop) {
//this list is empty very often but it shouldn't, that's the problem I am facing. I need to assure updates visibility.
List<PooledObject> idlePooledObjects = entry.getValue();
if (idlePooledObjects.isEmpty()) {
stop = true;
} else {
PooledObject pooledObject = null;
try {
pooledObject = idlePooledObjects.get(0);
info(loggingId, " - REMOOOVINNGG: \"", pooledObject.getClientId(), "\".");
PoolingStatus destroyStatus = poolManager.destroyIfExpired(pooledObject);
switch (destroyStatus) {
case DESTROY:
info(loggingId, " - Removed pooled object \"", pooledObject.getClientId(), "\" from pool: \"", poolManager.getClientId(), "\".");
idlePooledObjects.remove(0);
break;
case IDLE:
stop = true;
break;
default:
idlePooledObjects.remove(0);
break;
}
} catch (@SuppressWarnings("unused") PoolDestroyedException e) {
warn(loggingId, " - WARNING: Pooled object \"", pooledObject.getClientId(), "\" skipped, pool: \"", poolManager.getClientId(), "\" has been destroyed.");
synchronized(idleInstancesMap) {
it.remove();
}
stop = true;
} catch (PoolManagementException e) {
error(e, loggingId, " - ERROR: Errors occurred during the operation.");
idlePooledObjects.remove(0);
}
}
}
}
Thread.yield();
}
This is the method invoked (many times) by any other thread:
public void addPooledObject(PoolManager poolManager, PooledObject pooledObject) {
synchronized (idleInstancesMap) {
List<PooledObject> idleInstances = idleInstancesMap.get(poolManager);
if (idleInstances == null) {
idleInstances = Collections.synchronizedList(new LinkedList<PooledObject>());
idleInstancesMap.put(poolManager, idleInstances);
}
idleInstances.add(pooledObject);
}
}
Thanks
map
itselfvolatile
? Cannot you make itfinal
? – ThiloMap
does not define aniterator()
method (and neither doesConcurrentHashMap
). – daniumap
asvolatile
indicates that you probably don’t understand what this modifier does. The variablemap
should not change and it is best to demonstrate that it doesn’t by declaring the variablefinal
. – Holger