Java 集合 Fail-Fast 机制 VS Fail-Safe 机制

更多 Java 集合类方面的文章,请参见文集《Java 集合类》


Fail-Fast 机制

A fail-fast system is nothing but immediately report any failure that is likely to lead to failure. When a problem occurs, a fail-fast system fails immediately.
In Java, we can find this behavior with iterators. In case, you have called iterator on a collection object, and another thread tries to modify the collection object, then concurrent modification exception will be thrown. This is called fail-fast.

Fail-fast 机制是 Java 集合中的一种错误机制。 当多个线程对同一个集合的内容进行操作时,就可能会产生 Fail-fast 事件。

符合 Fail-Fast 机制的集合类包括:

  • Map:

    • HashMap
    • LinkedHashMap
    • TreeMap
    • Hashtable 虽然 Hashtable 是线程安全的
  • Set:

    • HashSet
    • LinkedHashSet
    • TreeSet
  • List:

    • ArrayList
    • LinkedList
    • Vector 虽然 Vector 是线程安全的
  • 使用 Collections.synchronizedXXX() 创建的线程安全的集合也是 Fail-fast

例如:

Map<String, String> m = new HashMap<String, String>();
m.put("A", "AA");
m.put("B", "BB");
m.put("C", "CC");

Iterator it = m.entrySet().iterator();
while (it.hasNext()) {
    m.put("D", "DD");

    Map.Entry<String, String> e = (Map.Entry) it.next();
    System.out.println(e.getKey() + e.getValue());
}

在通过 Iterator 遍历集合时,修改了集合的结构,因此抛出 ConcurrentModificationException:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMapHashIterator.nextNode(HashMap.java:1437) at java.util.HashMapEntryIterator.next(HashMap.java:1471)
at java.util.HashMap$EntryIterator.next(HashMap.java:1469)
at MapTest.main(MapTest.java:17)

Fail-fast 机制实现原理

通过 modCount (修改次数)域来实现。

在构造迭代器 Iterator 时,通过 expectedModCount 记录了当前的修改次数:

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 相等:

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。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
  • 如果只是修改了 Map 的值,没有修改 Map 的结构,则不会抛出 ConcurrentModificationException。例如将例子中的代码 m.put("D", "DD"); 修改为 m.put("A", "AAA");

Fail-Safe 机制

使用 java.util.concurrent.ConcurrentHashMap,无论改变值还是改变结构,都不会抛出 ConcurrentModificationException。

In this case the iterator will make a copy of the internal data structure and iterates over the copied data structure. Thus any modifications done to the internal data structure will not effect the iterator.
原理:在原集合的 copy 上遍历。
存在两个问题:

  • 创建 copy 需要额外的空间和时间上的开销
  • 不能保证遍历的是最新的内容

引用:
HashMap 的实现原理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最初的Java版本包含几种集合类:V...
    独念白阅读 801评论 0 2
  • 1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最初的Java版本包含几种集合类:V...
    hutuxiaogui阅读 680评论 0 10
  • 1.Java集合框架是什么?说出一些集合框架的优点?每种编程语言中都有集合,最初的Java版本包含几种集合类:Ve...
    yjaal阅读 1,185评论 1 10
  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光剑书架上的书阅读 3,923评论 2 8
  • 当爱已成殇,我轻声说“我依然爱你”而你没有听到。我在佛前苦苦哀求,佛要我用三生三世换你一世幸福,于是我化身成为佛祖...
    邪愚阅读 362评论 0 0