Java fail-fast 和 fail-safe机制

2017-10-30  本文已影响0人  ObadiObada

Java fail-fast 和 fail-safe机制

基本概念

fail-fastfail-safe这两个概念都是和集合的并发修改相关的。集合的并发修改是指:当一个线程正在遍历集合时,当另外 一个线程(或线程本身)修改了集合。

fail-fast

fail-fast 机制是指在遍历集合的过程中如果发现了集合被改变,则抛出ConcurrentModificationException。通常有如下两种情况:

  1. 遍历集合过程中本修改了集合
  2. 多线程的情况下,本线程遍历集合过程中,其他线程修改了集合。

fail-fast的检测

以ArrayList代码为例,从代码可以看出expectedModCount初始化为modCount, 其中modeCount是ArrayList被修改(add remove)的次数,iterator next 以及remove接口 都会检查expectedModCount和modCount是否相等,如果不等则抛出异常。

public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    protected int limit = ArrayList.this.size;

    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor < limit;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

一个例子:

ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
for (String str : list) {
    list.add("D") ;
}

输出结果:

Exception in thread "main" java.util.ConcurrentModificationException

at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)

at java.util.ArrayList$Itr.next(ArrayList.java:851)

at TestJava.main(TestJava.java:14)

这里隐含了一个知识点,对于foreach循环,javac编译器实际上是将其处理成iterator迭代。

fail-safe

fail-safe 机制是指在遍历集合的过程中如果发现了集合被改变,遍历集合依然可以正常进行,以CopyOnWriteArrayList为例:

public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}

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;

    COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;
    }
    //...
}

public boolean add(E e) {
    synchronized (lock) {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    }
}

public E remove(int index) {
    synchronized (lock) {
        Object[] elements = getArray();
        int len = elements.length;
        E oldValue = get(elements, index);
        int numMoved = len - index - 1;
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            Object[] newElements = new Object[len - 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
            setArray(newElements);
        }
        return oldValue;
    }
}

final void setArray(Object[] a) {
    elements = a;
}

从代码中可以看到:iterator返回的是COWIterator,COWIterator实际上遍历的是,当前的elements。而在每次都会将旧的元素拷贝到一个新的数组中然后再进行操作.

一个例子

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
for (String str : list) {
    System.out.print(str + "\r\n");
}

输出结果为:

A

B

C

上一篇下一篇

猜你喜欢

热点阅读