【问题标题】:Thread safety of iterator.next()iterator.next() 的线程安全
【发布时间】:2021-03-02 18:08:34
【问题描述】:

如果我有一个在多个线程之间共享的Iterator,每个线程都调用:

// Inside a thread
int myValue = iterator.next();

可能的结果是什么?

(忽略 next() 可能抛出 NoSuchElementException 的事实)如果迭代器是 ArrayList 上的迭代器,是否有可能多个线程最终在 myValue 变量中得到相同的值?

下面的代码是解决此问题的一种方法吗? (除了使用此处描述的 Java 8 流Passing a List Iterator to multiple Threads in Java)。

// Inside a thread
int myValue;
synchronized(iterator)
{
    myValue = iterator.next();
}

【问题讨论】:

  • 迭代器不应在线程之间共享。在另一个线程可能正在修改的集合上使用迭代器通常不是线程安全的

标签: java multithreading iterator thread-safety


【解决方案1】:

TLDR;切勿在线程之间共享迭代器!

考虑到循环内容最常见的迭代器用法,您可能会遇到以下 sn-p:

while(iterator.hasNext()) {
    Object nextItem = iterator.next();
}

现在考虑另一个线程执行完全相同操作的可能性。由于您无法控制线程调度,因此在具有单个元素的 Iterator 上可能会发生以下情况:

Thread 1: hasNext? true
Thread 2: hasNext? true
Thread 1: next() //but if this was the last element...
Thread 2: next() //...this will throw NoSuchElementException

迭代器也可能支持Iterator.remove(),当您对共享集合进行操作时,这可能会导致ConcurrentModificationException

我们能否在不同的线程中得到相同的值?

以与上述类似的方式,考虑这个非常简单的迭代器实现(简化代码):

class SimpleIterator implements Iterator {
    ArrayList source;
    int currentIndex;
    hasNext() {
        return currentIndex<source.size();
    }
    next() {
         Object o = source.get(currentIndex);
         currentIndex++;
         return o;
    }
}

在这里我们可能会得到:

Thread 1: get(currentIndex) //Object A
Thread 2: get(currentIndex) //also Object A
Thread 1: currentIndex++
Thread 2: currentIndex++  //you have skipped an element

这里的答案是是的,但重要的是要注意它在很大程度上取决于实现。根本不去那里更安全。

重复:一般来说,你不应该在线程之间共享迭代器。

【讨论】:

  • (忽略 next() 可能抛出 NoSuchElementException 的事实)如果迭代器是 ArrayList 上的迭代器,是否有可能多个线程最终可能在 myValue 变量中得到相同的值?
  • 是的,我认为这是可能的,具体取决于迭代器的实现。根据我的示例考虑类似的交错,然后考虑如果迭代器在内部简单地指向它在 ArrayList 中的当前索引会发生什么。由于双重检索然后索引的双重增量,您可能会重复并跳过下一个项目。
  • IMO,您可以重复最后一句话,“...从不共享迭代器...”,一次在前面,一次在结尾。
【解决方案2】:

是不是多个线程在 myValue 变量?

无法保证。

由于iterator 不是线程安全的,您应该在集合的对象上进行同步,例如

Iterator<String> iterator = obj.iterator();

synchronized (obj) {
    while (iterator.hasNext()) {
        int myValue = iterator.next();
        //...
    }
}

【讨论】:

  • 我在帖子中所做的同步迭代器(而不是集合)是否不正确?假设集合是ArrayList?
【解决方案3】:

List#iterator() 的行为在 List 实现中不一致

ArrayListLinkedList,如果在迭代过程中被修改,将抛出 ConcurrentModificationException。为避免这种情况,请使用 synchronizedList() 并在迭代期间锁定 List

Vector被默认同步,但Iterator不是线程安全的。

CopyOnWriteArrayList, we can iterate the List safely, even if concurrent modification is happening while iteration.

【讨论】:

  • (忽略 next() 可能抛出 NoSuchElementException 的事实)如果迭代器是 ArrayList 上的迭代器,是否有可能多个线程最终可能在 myValue 变量中得到相同的值?
猜你喜欢
  • 2017-02-10
  • 1970-01-01
  • 1970-01-01
  • 2011-03-30
  • 2022-11-21
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2015-06-01
相关资源
最近更新 更多