什么是快速失败(fail-fast)和安全失败(fail-safe)?

什么是快速失败(fail—fast)和安全失败(fail-safe)

一、快速失败(fail—fast)

  • 现象:在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception(并发修改异常)。

  • 原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。(源码很清晰)

  • 场景:java.util 包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

  • 普通集合容器源码中的Itr在集合采取该方法进行遍历时,
    cursor:调用next方法时,返回的元素的索引值
    lastRet:最近一次调用nextprevious的返回值,如果该元素已经被调用remove方法删除,则重置为-1
    modCount:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。 集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。

    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包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

三、测试代码:

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源码
在集合采取该方法进行遍历时,

        snapshot: 数组的快照

        cursor: 调用next方法时,返回的元素的索引值
       

因为遍历时是通过快照数组进行的遍历,不会影响原有的集合的任何操作

    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;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值