是否可以安全地迭代Set.toArray()返回的数组

时间:2015-08-26 02:40:00

标签: java multithreading

我有一个静态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);
    }
  }
}

2 个答案:

答案 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):

  

对于putAllclear等聚合操作,并发检索可能反映仅插入或删除某些条目。