是否必须同步并发集合



想象一下下面的例子:一个应用程序启动两个线程。Provider类保存并发集合并将数据写入其中。Consumer从集合中读取数据。

以下代码正确吗?还是必须添加同步?

public class Application{
public static void main(String...args) throws Exception{
Provider p = new Provider();
new Thread(p).start();
new Thread(new Consumer(p)).start();
// Make sure the example stops after 60 seconds
Thread.sleep(1000*60);
System.exit(0);
}
}
/**The Provider (writes data to concurrent collection)*/
class Provider implements Runnable{
private ConcurrentMap<Integer, String> map 
= new ConcurrentHashMap<Integer, String>(20, 0.5f, 1);
public void run(){
Integer i = 1;
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException ignore) {
}
// Synchronization ?
map.put(i, i.toString());
i++;
}
}
public ConcurrentMap<Integer, String> getMap(){
// Synchronization ?
return map;
}
}
/**The Consumer (reads data from concurrent collection)*/
class Consumer implements Runnable{
private Provider provider;
public Consumer(Provider p){
provider = p;
}
public void run(){
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
}
// Synchronization ?
ConcurrentMap<Integer, String> m = provider.getMap();
if(m!=null)
for(String s: m.values())
System.out.print(s); 
System.out.println();               
}
}
}

来自ConcurrentHashMap文档:

用于聚合操作例如putAllclear,并发检索可以仅反映某些条目的插入或删除。类似地,Iterators、Spliterators和Enumerations返回反映在创建迭代器/枚举。他们不抛出ConcurrentModificationException。然而,迭代器被设计为一次只能由一个线程使用。请记住,聚合状态方法的结果包括sizeisEmptycontainsValue通常只有当映射没有在其他线程中进行并发更新时才有用。否则,这些方法的结果会反映瞬态这可能足以用于监测或估计目的,但不足以用于程序控制。

所以您不需要进行同步,因为您不会得到ConcurrentModificationException。是否希望取决于您的程序逻辑。

并发集合的变体是线程安全的,但不能保证数据的一致性。

备用Collections.synchronized[...]习惯用法与显式同步相结合,将保证线程安全性数据一致性(以牺牲性能为代价)。

最新更新