美文网首页
fail-fast 与 fail-safe

fail-fast 与 fail-safe

作者: 米_8d62 | 来源:发表于2018-04-20 17:21 被阅读0次

    先说说什么是fail-fast

    fail-fast 机制是java集合(Collection)中的一种错误机制。在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception。

    fail-fast作用域

    java.util包下的所有集合类。

    fail-fast抛出ConcurrentModificationException

    1. 单线程情况下
      集合被创建后,在遍历它的过程中修改了原集合的结构。
    public class MyTest {
    
        static List<Integer> list;
        static {
            list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
            list.add(5);
            list.add(6);
        }
    
        public static void main(String[] args) {
    
            for (Integer i : list) {
                if (3 == i) {
                    list.remove(2);
                }
            }
    
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        }
    }
    
    
    1. 多线程情况下
      当一个线程在遍历这个集合,而另一个线程对这个集合的结构进行了修改。
    public class MyTest {
        private static List<Integer> list = new ArrayList<>();
    
        static class Thread1 extends Thread {
    
            @Override
            public void run() {
                Iterator<Integer> iterator = list.iterator();
                while(iterator.hasNext()) {
                    int i = iterator.next();
                    System.out.println("ThreadOne 遍历:" + i);
                }
            }
        }
    
        static class Thread2 extends Thread {
    
            @Override
            public void run() {
                int i = 0 ;
                while(i < 6){
                    System.out.println("ThreadTwo run:" + i);
                    if(i == 3){
                        list.remove(i);
                    }
                    i++;
                }
            }
        }
    
        public static void main(String[] args) {
            int i = 0;
            while(i < 10) {
                list.add(i);
                i ++;
            }
    
            Thread1 thread1 = new Thread1();
            Thread2 thread2 = new Thread2();
            thread1.start();
            thread2.start();
        }
        
    }
    

    fail-fast机制是如何检测的?

    迭代器在遍历过程中是直接访问内部数据的,因此内部的数据在遍历的过程中无法被修改。为了保证不被修改,迭代器内部维护了一个标记 “mode” ,当集合结构改变(添加和删除,修改不会改变集合结构),标记"mode"会被修改,而迭代器每次的hasNext()和next()方法都会检查该"mode"是否被改变,当检测到被修改时,抛出Concurrent Modification Exception

        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

    fail-safe 是针对 fail-fast的处理,在concurrent包下包含对集合的操作类


    concurrent.png

    fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException。
    fail-safe机制有两个问题
    (1)需要复制集合,产生大量的无效对象,开销大
    (2)无法保证读取的数据是目前原始数据结构中的数据。

    CopyOnWriteArrayList

    package java.util.concurrent;
    import java.util.*;
    import java.util.concurrent.locks.*;
    import sun.misc.Unsafe;
    
    public class CopyOnWriteArrayList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
        ...
    
        // 返回集合对应的迭代器
        public Iterator<E> iterator() {
            return new COWIterator<E>(getArray(), 0);
        }
    
        ...
       
        private static class COWIterator<E> implements ListIterator<E> {
            private final Object[] snapshot;
    
            private int cursor;
    
            private COWIterator(Object[] elements, int initialCursor) {
                cursor = initialCursor;
                // 新建COWIterator时,将集合中的元素保存到一个新的拷贝数组中。
                // 这样,当原始集合的数据改变,拷贝数据中的值也不会变化。
                snapshot = elements;
            }
    
            public boolean hasNext() {
                return cursor < snapshot.length;
            }
    
            public boolean hasPrevious() {
                return cursor > 0;
            }
    
            public E next() {
                if (! hasNext())
                    throw new NoSuchElementException();
                return (E) snapshot[cursor++];
            }
    
            public E previous() {
                if (! hasPrevious())
                    throw new NoSuchElementException();
                return (E) snapshot[--cursor];
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor-1;
            }
    
            public void remove() {
                throw new UnsupportedOperationException();
            }
    
            public void set(E e) {
                throw new UnsupportedOperationException();
            }
    
            public void add(E e) {
                throw new UnsupportedOperationException();
            }
        }
      
        ...
    
    }
    
    1. 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。
    2. ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。
    3. ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!

    fail-fast 和 fail-safe

    区别.png

    总结

    在对集合处理时,需要注意是否存在在遍历集合时是否改变集合结构,多线程情况下使用concurrent包下的类;

    相关文章

      网友评论

          本文标题:fail-fast 与 fail-safe

          本文链接:https://www.haomeiwen.com/subject/flgskftx.html