Fail-Fast,Fail-Safe,还有ConcurrentModificationException
甜点
关于HashMap里的Fail-Fast机制,我们先来看下这段代码
1 | ArrayList<String> list=new ArrayList<>(3); |
运行起来,就会抛出java.util.ConcurrentModificationException。原因是在遍历的同时改变了数组的内容。
换成这种形式会更好理解:
1 | ArrayList<String> list=new ArrayList<>(3); |
这里会抛出java.lang.IndexOutOfBoundsException。
概念
迭代器(Iterator)在工作的时候,会检查集合(Collection)的内容是否发生了变更,如果发生了变更,则抛出java.util.ConcurrentModificationException。
这个机制,叫做Fail-Fast.
常用的集合如:ArrayList、LinkedList、HashMap等,均有此机制的保护。
我们通过HashMap的Iterator的remove方法,来了解这个机制的工作方式:
1 | int expectedModCount; // for fast-fail |
第7行,这个迭代器会检测modCount和expectedModCount是否相等,在别的更多操作里,如next(),也会进行相同的检测。
更多
Fail-Safe的概念和Fail-Fast正相反:
如果一个集合的迭代器(Iterator)在工作的时候,对集合做数据的增删操作(包括多线程),也不会有影响,那么这个集合就是Fail-Safe的。
例如这段代码:
1 | CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<>(); |
跑起来就不会有问题。
原因正如它的名字一样:Copy.这个集合在增删的操作时候,会先加上ReentrantLock,然后再将数组的内容copy一份,操作copy的数组,最后再将数组写入回去。例如:CopyOnWriteArrayList、CopyOnWriteArraySet
。
另外一类的Fail-Safe集合的机制与此略有区别,逻辑也更为复杂,
目前有这些集合类是Fail-Safe的:ConcurrentHashMap、ConcurrentLinkedQueue等Concurrent开头的集合。
总结
避免ConcurrentModificationException的方法是:
对集合进行迭代操作的时候,不要变更集合的内容,也不能在别的线程操作集合。
如果确实需要面对这种业务场景,可以选择是Fail-Safe的集合。