首页 > 解决方案 > iterator.next() 的线程安全

问题描述

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

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

什么是可能的结果?

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

下面的代码是解决此问题的一种方法吗?(除了使用 Java 8 流,如此处所述将列表迭代器传递给 Java 中的多个线程)。

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

标签: javamultithreadingiteratorthread-safety

解决方案


TLDR;永远不要在线程之间共享迭代器!

考虑到迭代器最常见的用于循环内容的用途,您可能会遇到以下代码段:

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

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

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

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

重复:通常你不应该在线程之间共享迭代器。


推荐阅读