想象一下同步 Collection
:
Set s = Collections.synchronizedSet(new HashSet())
克隆此系列的最佳方法是什么?
首选克隆不需要在原始Collection上进行任何同步,但要求对克隆的Collection进行迭代不需要在原始Collection上进行任何同步。
答案 0 :(得分:7)
在synchronized块中使用copy-constructor:
synchronized (s) {
Set newSet = new HashSet(s); //preferably use generics
}
如果您还需要同步副本,请再次使用Collections.synchronizedSet(..)
。
根据彼得的评论 - 你需要在原始集合的同步块中执行此操作。 synchronizedSet
的文档明确说明了这一点:
当迭代它时,用户必须手动同步返回的集合
答案 1 :(得分:3)
使用同步集时,请注意您将导致访问集中每个元素的同步开销。 Collections.synchronizedSet()
只是用一个强制每个方法同步的shell包装你的集合。可能不是你真正想要的。 ConcurrentSkipListSet
将在多线程环境中为您提供更好的性能,其中多个线程将写入该集合。
ConcurrentSkipListSet
将允许您执行以下操作:
Set newSet = s.clone();//preferably use generics
使用集合的克隆进行快照处理并不罕见。如果这就是您所追求的,您可以添加一些代码来处理已经处理该项目的情况。包含在多个副本集中的偶然对象所涉及的开销通常低于使用Collections.concurrentSet()
的一致开销。
编辑:我刚注意到ConcurrentSkipListSet是Cloneable并提供了线程安全的clone()
方法。我改变了答案,因为我真的相信这是最好的选择 - 而不是失去Collections.concurrentSet()
的可扩展性和性能。
答案 2 :(得分:1)
您可以通过执行以下操作来避免同步集,从而避免在原始集上暴露迭代器。
Set newSet = new HashSet(Arrays.asList(s.toArray()));
从Collections.SynchronizedCollection编辑
public Object[] toArray() {
synchronized(mutex) {return c.toArray();}
}
如您所见,锁定将在执行操作的整个时间内保持。因此,采取了这种安全的数据副本。如果在内部使用Iterator并不重要。返回的数组可以以线程安全的方式使用,因为只有本地线程具有对它的引用。
注意:如果您想避免这些问题,我建议您使用2004年Java 5.0中添加的并发库中的Set。我还建议您使用泛型,因为这可以使您的集合更安全。