fail-fast

什么是fail-fast

fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件

或者你在单个线程对ArrayList边遍历边修改也会报错
在这里插入图片描述

fail-fast原理

像之前的文章所说,ArrayListArrayist继承了AbstractList里面的成员变量modCount,这个变量用来记录变化
再上面了例子中观察remove()的源码,发现remove的时候,modCount的值会改变
在这里插入图片描述
而在迭代器遍历的时候都会比较expectedModCount和modCount是否相等

 public Iterator<E> iterator() {
       return new Itr();
 }
    
  private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        // prevent creating a synthetic constructor
        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

不相等就会抛出ConcurrentModificationException()异常(add()、remove(),clear(),和其他涉及到修改集合中的元素个数的方法同理)

如何解决

  1. 如果是在单线程里想要一边遍历一遍去删除ArrayList的元素,可以使用迭代器的remove()方法,因为他会同步expectedModCount和modCount
public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
  1. java.util.concurrent包中使用CopyOnWriteArrayList解决fail-fast

① 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。

② ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。

③ ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!

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();
         }
     }
   
     ...
 }
发布了89 篇原创文章 · 获赞 0 · 访问量 2900
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 像素格子 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览