在执行Java 8流操作时集合正在被更新

6

我有一个对象列表,会不断地被几个线程更新。在更新时,我想使用流(Stream)来过滤掉一些元素。

例如,假设我有一个经常更新的列表:

List<MyObject> myList

现在,在某个时间点上,我使用流对该列表进行操作。

List<MyObject> result =  
myList.stream().filter(myobj->myobjt.isValid()).collect(toList());

如果我的列表会被多个线程同时更新,这段代码是否是线程安全的?


4
你使用哪种列表实现?你是如何更新列表(添加到末尾或其他操作)?你希望你的流对这些更新做什么(忽略/包含/快速失败/其他)? - Thilo
我使用CopyOnWriteArrayList,因为我想要线程安全的集合。我只在列表末尾添加元素。我希望我的流可以执行一般的操作,无论是过滤还是更新列表中的对象。我只是举了一个过滤的例子,但它可以是任何操作。 - user1409534
1个回答

7
CopyOnWriteArrayList的Javadoc声明如下:
“快照”类型的迭代器方法使用指向创建迭代器时数组状态的引用。在迭代器的生命周期内,该数组永远不会更改,因此干扰是不可能的,迭代器保证不会抛出ConcurrentModificationException异常。自创建迭代器以来,迭代器不会反映列表的添加、删除或更改。
所以,是的,您的代码应该是线程安全的。流将忽略在启动后对列表进行的所有添加。

我明白了...如果只有一个线程更新列表,而另一个线程在该列表上使用流操作呢? - user1409534
我不太明白快照样式如何“永远”不改变迭代器的基础数组。对COW的任何修改操作都将被“同步”,最终调用volatile列表上的setArray。因此,如果迭代器尚未到达该点,则仍可能看到修改。不是吗? - stdout
1
@stdout: 当CopyOnWriteArrayList被更新时,它会复制底层数组。迭代器将继续在旧的、未更改的数组上工作,这是它创建时的状态。 - Thilo
@Thilo 所以,即使通过 setArray 调用最终会覆盖原始副本,这也不会干扰 COWIterator 开始迭代的那个。 - stdout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接