4

获取java.util.Set同步版本的最简单方法是使用Collections.synchronizedSet(),如下所示:

Set mySyncSet = Collections.synchronizedSet(new HashSet());

Java API关于这个新对象的说法是:

当迭代它时,用户必须在返回的集合上手动同步

我的问题是,如果我使用这样的复制构造函数创建此Set的副本:

Set mySetCopy = new HashMap(mySyncSet);

它是线程安全的吗?(毕竟HashMap 构造函数不是使用迭代来获取Set的成员吗?)还是我应该像这样手动同步操作?:

Set mySetCopy;

synchronized(mySyncSet) {
    mySetCopy = new HashMap(mySyncSet);
}
4

2 回答 2

9

让我们看一下代码:

public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

所以这只是调用addAll

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

所以这个循环Collection你给它。

因此答案是否定的,构造函数副本不是线程安全的。

您需要使用第二个选项并在将其传递给构造函数之前对其进行显式synchronized处理。Set

于 2013-07-12T14:07:32.680 回答
0

第二种方式更可取。如果某个线程修改了您的原始线程,而该线程正在遍历您的集合以将引用复制到您的新集合,您将遇到麻烦。

所有访问或修改集合的代码都应该在同一个实例上手动同步……正如 JavaDoc 指定的那样。

于 2013-07-12T14:07:24.553 回答