[英]java - iterator.remove() if set is modified while iteration
我有一个由多个线程连续扩展的Set:
Set<String> concurrentSet = new CopyOnWriteArraySet<>();
有时我会处理集合中的元素,由于不再需要已处理的元素,因此可以使用iterator.remove()删除它们:
Iterator<String> i = concurrentSet .iterator();
while (i.hasNext()) {
String str = i.next();
//processing...
i.remove();
}
这是线程安全的吗? 如果线程在迭代时添加元素怎么办?
注意: 文档指出“如果在迭代进行过程中以其他方式(而不是通过调用此方法)修改基础集合,则未指定迭代器的行为。” => CopyOnWriteArraySet也是如此吗?
我认为这适用于集合不是线程安全的情况。 本来应该添加ConcurrentHashset来处理这种情况。
这取决于您使用的线程安全实现。 例如:
CopyOnWriteArraySet
javadoc指出:“ 迭代器不支持可变的remove操作。 ” =>如果调用it.remove()
则会出现异常。 ConcurrenSkipListSet
javadoc状态:“ 插入,删除和访问操作可以安全地由多个线程同时执行。迭代器是弱一致性的,在创建迭代器时或创建迭代器后的某个时刻返回反映集合状态的元素。 ” =>您可以放心假设调用it.remove()
是线程安全的(因为它不会引发ConcurrentModificationException)。 但是请注意:
if (it.hasNext()) {
it.next();
it.remove();
}
不是原子的,因此如果同时修改集合,该操作的结果可能是意外的。
CopyOnWriteArraySet
是线程安全的,但是Iterators
不支持可变删除操作,您可以使用CopyOnWriteArraySet#remove
删除ovject,但由于所有此可变操作(添加,设置,删除等)都非常昂贵,因此对该数据结构的操作非常昂贵操作完全复制整个基础数组。
从Java 7 JavaDoc( http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArraySet.html ):
[CopyOnWriteArraySet]迭代器不支持可变删除操作。
如果线程安全比性能更重要,我建议您沿Collections.synchronizedSet的方式使用。 使用起来比较困难,您必须使用迭代器将代码显式地包装在synceded(set){}块中,但是可以保证所有操作都是线程安全的。
如果需要同时进行并发和线程安全,则可以通过ReentrantReadWriteLock将普通的HashSet与自己的显式锁定一起使用。 如果正确实现,这将允许读取并发和写入线程安全。 我不知道Java API中有一个与此相关的标准类,但是那里可能有一个(或者实际上在其他地方,例如Apache Commons Collections)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.