我有一个静态Set
,我用它来缓存我的AType
实例。多个Worker
实例将在不同的线程上运行。 Workers
将迭代此Set
,根据每个AType
的属性执行操作。
(AType
的属性永远不会被修改。虽然Set
本身的内容可能会被修改)
如果我以线程安全的方式将Set
克隆到一个数组,那么它是否可以安全地迭代该数组?
(我不明白为什么它不会。在我做的测试中似乎是安全的。但是线程安全很难验证,并且从来没有伤害过要求......)
public class Worker {
private AType[] reusableArray;
private static Set<AType> theSet = new HashSet<>();
public Worker() {
//....
}
public void work() {
//....
doClone();
for(AType aType : reusableArray) {
// ...
}
}
private void doClone() {
synchronized (theSet) {
//.. do stuff, maybe populate the set, maybe remove items from it.
// try not to resize array unless necessary
if(reusableArray == null || reusableArray.length() != theSet.size()) {
reusableArray = new AType[theSet.size()];
}
theSet.toArray(reusableArray);
}
}
}
答案 0 :(得分:2)
toArray
将集合的内容复制到数组中。返回后,数组和集完全无关。
第一个线程调用toArray
后,你可以修改同一个线程中的集合,你可以在不同的线程中修改集合,你可以让集合被垃圾收集,或者你可以启动它到了木星。第一个线程不再使用该集合,并且它不关心它发生了什么。
(请注意,存储在数组中的引用与存储在集合中的引用相同,因此它们引用相同的对象,更改这些对象可能会引入线程安全问题。但是你说你没有这样做)
答案 1 :(得分:1)
不要经历所有这些箍,只需使用ConcurrentHashMap。
或者,使用Collections.synchronizedSet同步所有访问权限。
建议第一选择,但他们各有利弊。
主要是ConcurrentHashMap
在查询集合时会无法锁定(contains()
,迭代,...)。引用javadoc:
但是,即使所有操作都是线程安全的,检索操作不也需要锁定。
注意:没有ConcurrentHashSet
。要获得Set
,请按以下方式创建:
private static Set<AType> theSet = Collections.newSetFromMap(
new ConcurrentHashMap<AType, Boolean>());
瞧!快速,无阻塞的Set
仍然允许多线程访问,包括更新和查询。
只有警告(引用javadoc):
对于
putAll
和clear
等聚合操作,并发检索可能反映仅插入或删除某些条目。