Iterator迭代器和快速失败,安全失败

1,127 阅读4分钟

前段时间看了ArrayList的源码,里面讲到Iterator迭代器和快速失败,安全失败这块内容,以前没碰到过,学习了一番~   忘了好多,现在总结一下

(视频资源:www.bilibili.com/video/BV1gE…

一 Iterator迭代器

1.迭代器 Iterator 是什么?

Iterator 对象称为迭代器(设计模式的一种),迭代器可以对集合进行遍历,但每一个集合内部的数据结构可能是不尽相同的,所以每一个集合存和取都很可能是不一样的,虽然我们可以人为地在每一个类中定义 hasNext()next() 方法,但这样做会让整个集合体系过于臃肿。于是就有了迭代器。

迭代器是将这样的方法抽取出接口,然后在每个类的内部,定义自己迭代方式,这样做就规定了整个集合体系的遍历方式都是 hasNext()next()方法,使用者不用管怎么实现的,会用即可。迭代器的定义为:提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象的内部细节。

2. 迭代器 Iterator 有啥用?

Iterator 主要是用来遍历集合用的,它的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。

如果单单使用List集合里的remove方法,会 抛出 ConcurrentModificationException 异常,出现一些问题

迭代器的默认remove删除方法 it.remove() 直接删除(迭代器自带的remove方法),其实迭代器的remove方法底层还是用的arraylist的remove方法来删除元素,但是这个方法中删除的时候每次都会给预期修改次数的变量进行赋值,所以怎么都不会产生并发修改异常

二.快速失败和安全失败

1.什么是快速失败(fail-fast)?

快速失败(fail-fast) 是 Java 集合的一种错误检测机制。在使用迭代器对集合进行遍历的时候,我们在多线程下操作非安全失败(fail-safe)的集合类可能就会触发 fail-fast 机制,导致抛出 ConcurrentModificationException 异常。 另外,在单线程下,如果在遍历过程中对集合对象的内容进行了修改的话也会触发 fail-fast 机制。

注:增强 for 循环也是借助迭代器进行遍历。

举个例子:多线程下,如果线程 1 正在对集合进行遍历,此时线程 2 对集合进行修改(增加、删除、修改),或者线程 1 在遍历过程中对集合进行修改,都会导致线程 1 抛出 ConcurrentModificationException 异常。

为什么呢?

每当迭代器使用 hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedModCount 值,是的话就返回遍历;否则抛出异常,终止遍历。

如果我们在集合被遍历期间对其进行修改的话,就会改变 modCount 的值,进而导致 modCount != expectedModCount ,进而抛出 ConcurrentModificationException 异常。

注:通过 Iterator 的方法修改集合的话会修改到 expectedModCount 的值,所以不会抛出异常。

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

使用 Iterator 提供的 remove 方法,可以修改到 expectedModCount 的值。所以,才不会再抛出ConcurrentModificationException 异常。

2. 什么是安全失败(fail-safe)呢?

明白了快速失败(fail-fast)之后,安全失败(fail-safe)我们就很好理解了。

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。所以,在遍历过程中对原集合所作的修改并不能被迭代器检测到,故不会抛 ConcurrentModificationException 异常。

最后补充一点:迭代器的remove只能解决单线程下的异常抛出问题,多线程的问题还是得靠使用安全失败机制的集合容器。

最后,这篇文章只是总结了大概,并没有把之前看过的视频源码内容全部写进来,必要时可以再看一遍视频,然后看看源码~