众所周知,如果另一个线程碰巧同时修改了集合,那么在没有同步的情况下对集合进行迭代就有可能抛出ConcurrentModificationException
。
但是如何在集合上调用size()
呢? size()
是否涉及迭代来计算集合中元素的数量?如果是这种情况,可能会出现ConcurrentModificationException
(有些人报告此情况,例如here,但对我来说,不清楚size()是否是罪魁祸首。)
另一方面,如果size()
只获取内部int
计数器变量,则不会发生异常。
是哪一个?
HUMBLE EDIT:感谢回答者的精确度,这取决于实施情况。我应该提到它是一个TreeMap。我可以在这张地图上遇到这个问题吗?文档没有提到TreeMap.size()。
答案 0 :(得分:1)
如果您调用ConcurrentModificationException
,Collection#size
将会出现的文档don't explicitly state。
它被抛出的唯一真实时间are described in ConcurrentModificationException
itself:
当不允许进行此类修改时,检测到并发修改对象的方法可能抛出此异常。
例如,一个线程通常不允许修改Collection而另一个线程正在迭代它。通常,在这些情况下,迭代的结果是不确定的。如果检测到此行为,某些Iterator实现(包括JRE提供的所有通用集合实现的实现)可能会选择抛出此异常。执行此操作的迭代器称为失败快速迭代器,因为它们快速而干净地失败,而不是在未来的未确定时间冒着任意的,非确定性行为的风险。
如果您真的担心此类问题,请确保您的收藏集的具体实施使用适当的size()
方法,例如ConcurrentLinkedQueue
。
返回此队列中的元素数。如果此队列包含多个Integer.MAX_VALUE元素,则返回Integer.MAX_VALUE。 请注意,与大多数集合不同,此方法不是常量操作。由于这些队列的异步性质,确定当前元素数需要O(n)遍历。
此外,如果在执行此方法期间添加或删除元素,则返回的结果可能不准确。因此,这种方法在并发应用程序中通常不是很有用。
答案 1 :(得分:1)
size()
是否会抛出ConcurrentModificationException
并不重要。几乎没有任何集合会在size()
上抛出该异常,但这并不能使其安全。
当您在其他线程修改的集合上调用size()
时,您正在读取共享内存。如果您使用的集合没有提供特定的同步保证并且您没有使用其他同步机制,则您读取的值可能会随意过时,或者甚至可能对应于程序状态你会想到"还没有发生"。