什么是快速失败(fail-fast)
“快速失败”也就是 fail-fast,它是 Java 集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。记住是有可能,而不是一定。例如:假设存在两个线程(线程 1、线程 2),线程 1 通过 Iterator 在遍历集合 A 中的元素,在某个时候线程 2 修改了集合 A 的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生 fail-fast 机制。
一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。在迭代的时候,快速失败将尽最大努力抛出 ConcurrentModificationException。
实现原理
通过 modCount
(修改次数)域来实现。
1 2 3 4 5 6 7 8 9 |
HashIterator() { expectedModCount = modCount; Node<K,V>[] t = table; current = next = null; index = 0; if (t != null && size > 0) { // advance to first entry do {} while (index < t.length && (next = t[index++]) == null); } } |
在通过 put 方法修改 Map 时,将 modCount
(修改次数)域加 1: ++modCount;
。
在依次遍历迭代器 Iterator 时,判断 expectedModCount
是否与 modCount
相等:
1 2 3 4 5 6 7 8 9 10 11 12 |
final Node<K,V> nextNode() { Node<K,V>[] t; Node<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); if ((next = (current = e).next) == null && (t = table) != null) { do {} while (index < t.length && (next = t[index++]) == null); } return e; } |
如果不相等,就会抛出异常:ConcurrentModificationException
意义
防止在多线程下对于集合修改和读取的不确定性,提醒程序员要注意线程安全的问题。
如何避免
- 避免在多线程环境下使用非线程安全的集合类;
- 在多线程环境下使用concurrent包下面的集合类,避免多线程问题。