2014-09-28 103 views
1

我具有由多线程共享的访问的对象的ConcurrentHashMap访问的的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,它也是该条目的关键。

+2

这很可能是以下内容的重复。总之,你可以使用迭代器,你需要调用这个迭代器(不是地图)上的remove方法:http://stackoverflow.com/questions/3768554/is-iterating-concurrenthashmap-values-thread-safe – eckes 2014-09-28 19:52:04

+1

@eckes我不同意。这个问题的接受答案声称迭代ConcurrentHashMap通常是安全的,对于给定的“通常”值是正确的。但凯文的用法是不安全的,所以他是正确的问一个单独的问题。 – MikeFHay 2014-09-28 20:01:05

+0

感谢您的回复。我当然会从提到的另一个问题@eckes中获得一些提示。我的要求很简单。我需要的只是安全地移除物体并使地图保持有效状态。如果另一个线程在另一个线程循环时添加了新条目,则可以将新添加的条目留在地图中。如果是这样,上面的循环是安全的吗?或者我必须使用另一个问题中提到的迭代器? – Kevin 2014-09-28 20:12:25

回答

1

您的方法当前不安全,因为您的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(); 
    if (c != null) 
    { 
     c.close(); 
    } 
    it.remove(); 
} 
+0

感谢您的回复。是。我应该把空检查。但这不是我主要关心的问题。 myMap.values()仅仅是地图值的“视图”。当地图更改时,值()也会更改。我的问题是,如果在其他线程仍在添加时从迭图中移除集合,是否安全? – Kevin 2014-09-28 20:00:52

+0

那么是的,它是安全的,符合您的要求。从ConcurrentHashMap apidoc:'Iterators ...返回反映哈希表状态的元素在迭代器/枚举创建时或之后的某一时刻http://docs.oracle.com/javase/7/docs/api /java/util/concurrent/ConcurrentHashMap.html – MikeFHay 2014-09-28 20:08:11

0

迭代/循环播放你所呈现的方式不是线程安全的,因为你并没有迭代最新的地图。但这不是你关心的问题,因为你只是从hashmap中删除一个元素 - 而且这个线程是安全的(由于ConcurrentHashMap)。不需要锁定或同步它是没有意义的。

无论你迭代的是,因为你只有删除就OK了,你写道:

循环只需要在时间 myMap.keySet删除所有元素()被调用。

你的代码就好了。您可能需要检查您的连接在关闭之前是否为空(如建议)