快速失败(fail-fast)和安全失败(fail-safe)

快速失败(fail-fast):在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。

获取一个迭代器:

	 ArrayList<String> list = new ArrayList<String>();
	 list.add("a");
	 list.add("b");
	 ist.add("c");
	 list.add("d");
	 Iterator iter = list.iterator();
  	 while(iter.hasNext()) {
            value = (String)iter.next();
            System.out.print(value+", ");
         }

Ctrl+A查看iterator,实现方式:

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

这里返回了一个Itr类

private class Itr implements Iterator<E> {
        int cursor;      // 返回下一个元素的索引
        int lastRet = -1; // 返回最后一个元素的索引,如果没有就这样
        int expectedModCount = modCount; //预期修改数
        
	//省略其他代码。。。
	
	/**
		每次进行增删改查时比较modCount 和expectedModCount是否相等,
		不相等则抛出ConcurrentModificationException产生Fail-Fast
	*/
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
}

modCount是ArrayList继承AbstractList父类得来的值

protected transient int modCount = 0; //用来记录list的实际修改次数,每次修改modCount+1

那么怎么解决呢?
将ArrayList替换成java.util.concurrent包下对应的类,这时候就要用到安全失败了。

安全失败(fail-safe):采用安全失败机制的集合容器,在遍历时不是直接集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

	 CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
	 list.add("a");
	 list.add("b");
	 ist.add("c");
	 list.add("d");
	 Iterator iter = list.iterator();
  	 while(iter.hasNext()) {
            value = (String)iter.next();
            System.out.print(value+", ");
         }

Ctrl+A查看iterator,实现方式:

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

这里返回了一个COWIterator类

static final class COWIterator<E> implements ListIterator<E> {
	 private COWIterator(Object[] elements, int initialCursor) {
	 	  /**
	 	    调用iterator()时将新建COWIterator,
	 	   会将元素保存在snapshot 这个拷贝的数组中,我们在对老数据进行修时,
	 	    拷贝的数据不会变
	 	   */
	       cursor = initialCursor;
	       snapshot = elements;
	}
 }

比较他们的实现原理

在这里插入图片描述

特别注意
java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),迭代器的快速失败行为应该仅用于检测程序错误

java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差,但是读操作因为操作的对象和写操作不是同一个对象,读之间也不需要加锁,读和写之间的同步处理只是在写完后通过一个简单的“=”将引用指向新的数组对象上来,这个几乎不需要时间,这样读操作就很快很安全,适合在多线程里使用,绝对不会发生ConcurrentModificationException,所以最后得出结论:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

一部分是看别人理解得来的,一部分是自己理解的,有什么不足的地方希望可以指出来…Thanks♪(・ω・)ノ

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值