【发布时间】:2020-08-22 18:34:44
【问题描述】:
我正在使用一个线程(我们称之为“MapChecker”),它在 ConcurrentHashMap 的整个生命周期内循环。
地图由其他线程填充,并由 MapChecker 使用迭代器对其进行清除。
地图结构如下:
private volatile Map<MyObject, SynchronizedList<MyOtherObject>> map = new ConcurrentHashMap<>();
//SynchronizedList = Collections.syncrhonizedList.
MapChecker 必须更新其循环内每个键的值。通过从列表中删除元素或删除完整的地图条目来进行更新。
同步发生在两个步骤中:
- 在地图内添加数据时(同步)
- 检索地图的迭代器时(在 MapChecker 中同步)。
锁在地图本身上( synchronized(map) )。
我不关心在我的迭代器视图中总是有最后更新的值,但我需要确保在下一次迭代中检索到所有缺失的值,这很重要,因为我不想跳过任何元素。此外,正确更新 SynchronizedList 也很重要。
我的问题是: 我可以确保通过拥有这种架构来插入/更新所有条目吗?有错过某些东西的风险吗? 如果 MapChecker 删除了一个条目,而另一个线程正在更新同一个条目,会发生什么? ConcurrentHashMap 应该阻止这些操作,所以我预计不会有麻烦。
这是 MapChecker 循环:
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();
}
这是任何其他线程(多次)调用的方法:
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);
}
}
谢谢
【问题讨论】:
-
为什么
map本身是volatile?你不能做到final吗? -
如何在地图上获得迭代器?
Map没有定义iterator()方法(ConcurrentHashMap也没有)。 -
同步的“当检索地图的迭代器时”。你的意思是整个迭代都在关键部分?还是只是检索迭代器本身?
-
嗨@daniu,我在地图的entrySet上使用了一个迭代器。
-
重点是,您将变量
map声明为volatile表明您可能不了解此修饰符的作用。变量map不应该改变,最好通过声明变量final来证明它没有改变。
标签: java concurrency synchronized concurrenthashmap