【问题标题】:Thread-safe iteration over a collection集合上的线程安全迭代
【发布时间】:2011-05-29 21:27:18
【问题描述】:

我们都知道,当使用Collections.synchronizedXXX(例如synchronizedSet())时,我们会获得底层集合的同步“视图”。

但是,这些包装器生成方法的文档声明我们必须在使用迭代器迭代集合时显式地在集合上同步

您选择哪个选项来解决这个问题?

我只能看到以下几种方法:

  1. 按照文档所述进行操作:在集合上同步
  2. 在调用iterator()之前克隆集合
  3. 使用迭代器是线程安全的集合(我只知道CopyOnWriteArrayList/Set)

还有一个额外的问题:使用同步视图时 - 使用 foreach/Iterable 线程安全吗?

【问题讨论】:

  • 或者可能通过使用 ExecutorService 等来避免共享使用迭代器(使用迭代器将新的 Callable/Runnable 添加到 ExecutorService)。其他一些集合类型将自己标榜为“线程安全”我想知道他们的迭代器是否:stackoverflow.com/a/3362063/32453(我怀疑它)

标签: java multithreading collections iterator thread-safety


【解决方案1】:

您确实已经回答了您的额外问题:不,使用增强的 for 循环安全 - 因为它使用迭代器。

至于哪种方法最合适——这真的取决于你的上下文:

  • 写入频率是否非常低?如果是这样,CopyOnWriteArrayList 可能是最合适的。
  • 集合是否相当小,并且迭代很快? (即您在循环中没有做太多工作)如果是这样,同步可能会很好 - 特别是如果这种情况不经常发生(即您不会对集合有太多争用)。
  • 如果您正在做大量工作并且不想阻止其他线程同时工作,那么克隆集合的成功可能是可以接受的。

【讨论】:

【解决方案2】:

取决于您的访问模式。如果并发低,写频繁,1 性能最好。如果您有高并发和不频繁写入,则 3 将具有最佳性能。选项 2 在几乎所有情况下都会表现不佳。

foreach 调用iterator(),所以完全相同的事情适用。

【讨论】:

    【解决方案3】:

    您可以使用 Java 5.0 中添加的新集合之一,该集合支持迭代时的并发访问。另一种方法是使用线程安全的 toArray 进行复制(在复制期间)。

    Collection<String> words = ...
    // enhanced for loop over an array.
    for(String word: words.toArray(new String[0])) {
    
    }
    

    【讨论】:

      【解决方案4】:

      我可能完全不同意您的要求,但如果您不了解这些要求,请查看 google-collections 并牢记“支持不变性”。

      【讨论】:

        【解决方案5】:

        我建议删除Collections.synchronizedXXX 并在客户端代码中统一处理所有锁定。基本集合不支持在线程代码中有用的那种复合操作,即使您使用java.util.concurrent.*,代码也更加困难。我建议尽可能多地保留与线程无关的代码。尽量减少困难且容易出错的线程安全(如果我们很幸运的话)代码。

        【讨论】:

          【解决方案6】:

          您的所有三个选项都将起作用。为您的情况选择合适的选项将取决于您的情况。

          CopyOnWriteArrayList 如果您想要一个列表实现,并且您不介意每次编写时都复制底层存储,那么CopyOnWriteArrayList 将起作用。只要您没有非常大的集合,这对于性能来说非常好。

          ConcurrentHashMap 或“ConcurrentHashSet”(使用Collections.newSetFromMap)如果您需要MapSet 接口将起作用,显然您不会通过这种方式获得随机访问。一个伟大的!关于这两者的一点是,它们可以很好地处理大型数据集——当发生突变时,它们只会复制底层数据存储的一小部分。

          【讨论】:

            【解决方案7】:

            它确实取决于实现克隆/复制/toArray()、new ArrayList(..) 等获取快照所需的结果,并且 锁定集合。 使用 synchronized(collection) 并在迭代结束时通过 ensure 进行迭代将不会被修改,即有效地锁定它。

            旁注:(当内部需要创建一个临时 ArrayList 时,通常首选 toArray(),但有一些例外)。另请注意,除了 toArray() 之外的任何内容也应包含在 synchronized(collection) 中,使用 Collections.synchronizedXXX 提供。

            【讨论】:

              【解决方案8】:

              这个问题很老了(对不起,我有点晚了..)但我仍然想添加我的答案。

              我会选择你的第二个选择(即在调用 iterator() 之前克隆集合),但有一个重大的转折。

              假设,您想使用迭代器进行迭代,您不必在调用 .iterator() 之前复制 Collection 并否定(我松散地使用术语“否定”)迭代器模式的想法,但是你可以写一个“ThreadSafeIterator”。

              它可以在相同的前提下工作,复制集合,但不让迭代类知道,你就是这样做的。这样的迭代器可能如下所示:

              class ThreadSafeIterator<T> implements Iterator<T> {
                  private final Queue<T> clients;
                  private T currentElement;
                  private final Collection<T> source;
              
                  AsynchronousIterator(final Collection<T> collection) {
                      clients = new LinkedList<>(collection);
                      this.source = collection;
                  }
              
                  @Override
                  public boolean hasNext() {
                      return clients.peek() != null;
                  }
              
                  @Override
                  public T next() {
                      currentElement = clients.poll();
                      return currentElement;
                  }
              
                  @Override
                  public void remove() {
                      synchronized(source) {
                          source.remove(currentElement);
                      }
                  }
              }
              

              更进一步,您可能会使用Semaphore 类来确保线程安全或其他东西。但是要采取带一粒盐的删除方法。

              关键是,通过使用这样的迭代器,没有人,无论是迭代还是被迭代的类(这是一个真正的词)都不必担心线程安全。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-02-08
                • 2013-12-10
                • 2014-03-18
                • 2011-04-15
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多