我有一个ConcurrentHashMap对象,由多个线程共享来访问:
Map<String, MyConnection> myMap = new ConcurrentHashMap<String, MyConnection>();
MyConnection类包含与数据源的一些连接。
稍后,我需要迭代ConcurrentHashMap,调用MyConnection.close()方法并将其从Map中删除。像这样:
for(String key : myMap.ketSet()) {
MyConnection connection = myMap.get(key);
connection.close();
myMap.remove(key);
}
但是,myMap由多个线程共享,可以添加,同时从ConcurrentHashMap中删除。
如何确保上面的for循环是否可以运行?不要求循环必须删除映射中的所有条目。循环只需要在调用myMap.keySet()时删除所有元素。不必删除添加到地图中的任何后续元素。
显然我可以锁定整个for循环并防止其他线程触及地图,但我认为它不具备性能效率。
有人可以分享您的意见吗?
EDIT1 :这个怎么样?这个线程安全吗?
for(MyConnection connection : myMap.values()) {
connection.close();
myMap.remove(connection.getId());
}
每个连接对象都有一个ID,它也是该条目的关键字。
答案 0 :(得分:0)
您的方法目前不安全,因为您的connection
可能是null
(如果另一个线程在您进行迭代时将其删除),则可能会抛出NullPointerException。只需添加一个空检查,您的方法是正确的,并且是线程安全的。
如果你关注性能,你可能想简单地迭代map.values()并以这种方式删除变量,每次迭代可以节省两次地图查找:
//the following code has not been compiled, and may be incorrect
Iterator<MyConnection> it = myMap.values().iterator();
while(it.hasNext())
{
MyConnection c = it.next();
c.close();
it.remove();
}
答案 1 :(得分:0)
迭代/循环你呈现的方式不是线程安全的,因为你没有迭代最新的地图。但这不是你的问题,因为你只是从hashmap中删除一个元素 - 并且IS线程安全(由于ConcurrentHashMap)。无需锁定或同步它没有任何意义。
你的两次迭代都没问题,因为你只删除了它并写了:
循环只需要删除当时的所有元素 myMap.keySet()被调用。
你的代码很好。在关闭之前,您可能需要检查连接是否为空(如建议的那样)
答案 2 :(得分:0)
正确的方法是使用remove的返回值:
for(String key : myMap.ketSet()) {
MyConnection connection = myMap.remove(key);
if (connection != null)
connection.close();
}