Java 集合的 fast-fail 机制,会抛出 ConcurrentModificationException
:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
从异常类的名称来看,似乎是多线程并发错误。但是,设计该异常类的作者并没有说只有多线程的并发错误才能抛出该异常:
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
下划线的部分意思是:如果单个线程发出的一系列方法调用违反了对象的契约,则该对象可能会抛出此异常。
所以我们在讨论为什么 modCount
没有加 volatile
字段保证原子性、可见性之类的多线程问题,并没有什么意义。因为本身就不是一个线程安全的类,设计的意义也不是硬性的保证能够及时抛出异常,所以不会使用 volatile
增加读写的代价。
fail-fast 机制是确保集合在使用迭代器遍历的过程中,不会出现除迭代器自己 remove()
和 add()
方法之外的任何对结构进行修改的方式。
实现方式使用了乐观锁的思想,但是并不是乐观锁。
讨论乐观锁往往在多线程中,fail-fast 机制并不是一定在多线程场景下。
ArrayList
的字段 modCount
类似乐观锁的版本号,迭代器在创建的时候后会生成自己的一个属性 expectedModCount
,该属性与 modCount
相等。
迭代器每次成功执行 remove()
, add()
时,都会将 ArrayList
的 modCount
赋值给自己的 expectedModCount
。
但是,每次在执行 remove()
, add()
之前都会校验 expectedModCount
与 modCount
是否相等,如果不相等,则抛出 ConcurrentModificationException