个人深入理解java集合框架:fail-fast

fail-fast

  • 认识fail-fast (快速失败)
    当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
  • 写个例子:
public static void main(String[] args) {
    ArrayList test1 = new ArrayList();
    for(int i =0;i<10;i++){
        test1.add(“a" + i);
    }

    Thread testa = new Thread(){
        @Override
        public void run() {
            for (int i =0 ;i<7 ;i++){
                System.out.println("testa---- 运行a");
                test1.add(1a"+i);
            }
        }
    };
    Thread testb =new Thread(){
        @Override
        public void run() {
            for (int i =0 ;i<7 ;i++){
                System.out.println("testb---- 运行b");
                test1.add(2b"+i);
            }
        }
    };
    Thread testc =new Thread(){
        @Override
        public void run() {
            for (int i =0 ;i<7 ;i++){
                System.out.println("testc---- 运行c");
                test1.iterator().next();
            }
        }
    };
    testa.start();
    testb.start();
    testc.start();
}

上述例子中:使用多个线程去加载执行ArrayList,添加操作、next操作都并行处理,就会出现ConcurrentModificationException异常。
在这里插入图片描述
出现异常的原因则是因为:next操作中进行了一个检查操作;

ublic 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;

    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];
    }
    ………………
    
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

在checkForComodification方法中进行了一个检查,modCount和expectedModCount的比较;

  • modCount :
/**
 * The number of times this list has been <i>structurally modified</i>.
 * Structural modifications are those that change the size of the
 * list, or otherwise perturb it in such a fashion that iterations in
 * progress may yield incorrect results.
 *
 * <p>This field is used by the iterator and list iterator implementation
 * returned by the {@code iterator} and {@code listIterator} methods.
 * If the value of this field changes unexpectedly, the iterator (or list
 * iterator) will throw a {@code ConcurrentModificationException} in
 * response to the {@code next}, {@code remove}, {@code previous},
 * {@code set} or {@code add} operations.  This provides
 * <i>fail-fast</i> behavior, rather than non-deterministic behavior in
 * the face of concurrent modification during iteration.
 *
 * <p><b>Use of this field by subclasses is optional.</b> If a subclass
 * wishes to provide fail-fast iterators (and list iterators), then it
 * merely has to increment this field in its {@code add(int, E)} and
 * {@code remove(int)} methods (and any other methods that it overrides
 * that result in structural modifications to the list).  A single call to
 * {@code add(int, E)} or {@code remove(int)} must add no more than
 * one to this field, or the iterators (and list iterators) will throw
 * bogus {@code ConcurrentModificationExceptions}.  If an implementation
 * does not wish to provide fail-fast iterators, this field may be
 * ignored.
 */
protected transient int modCount = 0;

从注释中可以看出modCount是表示:此list被修改的次数;在next、remove、previous方法中会出现ConcurrentModificationException异常。

在ArrayList中许多方法都会去操作modCount,例如:add、remove、clear等等。

我们可以想象在多线程操作中:
在这里插入图片描述
a线程完成了删除某个元素的操作,b线程正在遍历、c线程也准备删除某个元素;又恰好c线程要删除的元素就是a线程已经删除的元素,那么c线程将无元素可删除;同时b线程在a线程删除前已经遍历过该元素了,但是实际上元素List并没有该元素,这是不正确的。这样就会造成数据的混乱。

这就是一个简单的fail-fast事件。

有快速失败(fail-fast),就会有安全失败(fail-safe)。

fail-safe

是指:在对数据操作的情况下,对于数据操作进行拷贝操作,你可以简单的理解为将数据复制一份,对数据的操作是在拷贝数据的基础上操作的,但是对原数据修改你是不知道的,因此在安全失败机制中,你可以并发读取,不会抛出异常,但是不能保证当前读取的数据和对象集合的数据是一致的。

从另一个角度来理解的话:

在这里插入图片描述有a、b两个线程同时读取对象集合list,a线程遍历List是获取了一份当前快照,然后b线程对数据进行修改也是获取了当前快照;然后b线程变更了数据的内容,a线程继续遍历List,此时a线程是不会知道b线程的修改操作,因此两个线程操作的是各自的快照版本,是内存中不同的两个地址。

java集合中:

fail-fastfail-fast
ArrayList、HashMap、HashSet、VectorCopyOnWriteArrayList、ConcurrentHashMap

参考资料:
面试笔记–Fast-Fail(快速失败)机制

面试题思考:java中快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值