如何克隆同步集合?

时间:2011-01-03 13:02:24

标签: java collections synchronized

想象一下同步 Collection

Set s = Collections.synchronizedSet(new HashSet())

克隆此系列的最佳方法是什么?

首选克隆不需要在原始Collection上进行任何同步,但要求对克隆的Collection进行迭代不需要在原始Collection上进行任何同步。

3 个答案:

答案 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。我还建议您使用泛型,因为这可以使您的集合更安全。