什么是快速失败(fail---fast)和安全失败(fail-safe)
一、快速失败(fail---fast)
-
现象:在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception(并发修改异常)。
-
原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。(源码很清晰)
-
场景:java.util 包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。
-
普通集合容器源码中的Itr在集合采取该方法进行遍历时,
cursor
:调用next
方法时,返回的元素的索引值
lastRet
:最近一次调用next
或previous
的返回值,如果该元素已经被调用remove
方法删除,则重置为-1
modCount
:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个modCount
变量。 集合在被遍历期间如果内容发生变化,就会改变modCount
的值。每当迭代器使用hashNext()/next()
遍历下一个元素之前,都会检测modCount
变量是否为expectedmodCount
值,是的话就返回遍历;否则抛出异常,终止遍历。
java
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
二、安全失败(fail-safe)
现象:在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则不会抛出 Concurrent Modification Exception。
原理:采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。
三、测试代码:
java
public class ListDemo01 {
public static void main(String[] args) {
// 创建集合对象
List<String> list = new ArrayList<>() ;
list.add("张三") ;
list.add("李四") ;
list.add("王五") ;
// 遍历
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String e = it.next();
if(e.equals("王五")){
list.remove(e) ;
}
}
// 输出
System.out.println(list);
}
}
public class CopyOnWriteArrayListDemo01 {
public static void main(String[] args) {
// 创建集合对象
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>() ;
list.add("张三") ;
list.add("李四") ;
list.add("王五") ;
// 遍历
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String e = it.next();
if(e.equals("王五")){
list.remove(e) ;
}
}
// 输出
System.out.println(list);
}
}
安全失败机制的集合容器中的Itr源码
在集合采取该方法进行遍历时,
java
snapshot: 数组的快照
cursor: 调用next方法时,返回的元素的索引值
因为遍历时是通过快照数组进行的遍历,不会影响原有的集合的任何操作
java
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
@SuppressWarnings("unchecked")
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code remove}
* is not supported by this iterator.
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code set}
* is not supported by this iterator.
*/
public void set(E e) {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code add}
* is not supported by this iterator.
*/
public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
Object[] elements = snapshot;
final int size = elements.length;
for (int i = cursor; i < size; i++) {
@SuppressWarnings("unchecked") E e = (E) elements[i];
action.accept(e);
}
cursor = size;
}
}