【发布时间】:2016-05-18 18:56:33
【问题描述】:
我想将类的不可修改视图(维护项目集合)返回给外部客户端。
所以为了保护并发访问,我需要先将集合包装在一个同步包装器中,然后在我返回到外部线程的版本周围放置一个不可修改的包装器。
所以我编写了以下代码,不幸的是它抛出了 ConcurrentModificationException。 .
import java.util.*;
public class Test {
public static void main(String[] args) {
// assume c1 is private, nicely encapsulated in some class
final Collection col1 = Collections.synchronizedCollection(new ArrayList());
// this unmodifiable version is public
final Collection unmodcol1 = Collections.unmodifiableCollection(col1);
col1.add("a");
col1.add("b");
new Thread(new Runnable() {
public void run() {
while (true) {
// no way to synchronize on c1!
for (Iterator it = unmodcol1 .iterator(); it.hasNext(); it.next())
;
}
}
}).start();
while (true) {
col1 .add("c");
col1 .remove("c");
}
}
}
所以我的问题是如何同步不可修改的集合?
添加更多
当收到集合的客户端想要迭代其元素时
1) 它不一定知道它是一个同步的集合和
2) 即使这样做,它也无法在同步包装互斥锁上正确同步以迭代其元素。处罚,如中所述 Collections.synchronizedCollection,是不确定的行为。
据我了解 在同步集合上放置不可修改的包装器会导致无法访问 到必须保持正确迭代的互斥体。
【问题讨论】:
-
看看你能不能只返回一个副本/快照。让事情变得更容易
-
@ShowStopper,我不明白你在问题中添加了什么。 2.客户端不用同步,JVM和Iterator替他做。
-
Collections.java:2030 public Iterator
iterator() { return c.iterator(); // 必须由用户手动同步! }。如您所见,您的尝试不会阻止 CME,因为迭代器未同步。如果你想让迭代器同步,你可能需要自己包装它。但是,该迭代器将是不确定的,因为在 next() 之前添加元素意味着客户端不会触摸它。 -
您不需要同步不可修改的集合,因为它是不可修改的。在您的情况下,该集合是可修改的,因此您需要使用一个集合,即使它正在被修改,您也可以对其进行迭代,例如CopyOnWriteArrayList
标签: java multithreading collections immutability