Fail-Fast( 快速失败) 与 Fail-Safe(安全失败)原理

Fail-Fast 与 Fail-Safe

  • ArrayList 是 fail-fast 的典型代表,遍历的同时不能修改,尽快失败
  • CopyOnWriteArrayList 是 fail-safe 的典型代表,遍历的同时可以修改,原理是读写分离

ArrayList的底层原理:

ArrayList有个属性modCount,每次add()都让会modCount+1, remove()都是modCount-1; ArrayList 的遍历对象Iterator维护了一个expectedModCount属性, 执行第一次遍历时会把modCount的 值赋给 expectedModCount, 接着每次遍历都会判断modCountexpectedModCount是否相等,如果不相等:说明ArrayList遍历时添加或者删除了元素,此时会抛出ConcurrentModificationException异常。

CopyOnWriteArrayList的底层原理:

CopyOnWriteArrayList中存储元素的对象时一个Object[] array,CopyOnWriteArrayList的遍历对象COWIterator对象中维护了一个Object[] snapshot对象。
第一次遍历时便会让snapshot指向array的引用,接下来的遍历都是直接遍历snapshot副本。 并且CopyOnWriteArrayList每次添加元素时都会用创建一个新的array,所以执行遍历过程中如果CopyOnWriteArrayList中并发加入了或者删除了元素,其实此次遍历是无法感知到的,因为CopyOnWriteArrayList遍历其实是对快照对象(snapshot)的遍历

Fail-Fast( 快速失败)

java代码

public class FailFastAndFailSafeDemo {

    @Test
    public void testFailFast() {
        ArrayList<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        for (String str : list) {
            System.out.println(str);
        }
        System.out.println(list);
    }
}

1.list中有4个元素,第一次进入遍历代码,modCount为4
在这里插入图片描述

  1. 在遍历时,foreach其实就是调用了Iteratror对象中的迭代方法, (foreach其实为语法糖) , 此处将modCount的值赋给了expectedModCount,后面如果list中添加数据只会改变modCountexpectedModCount并不会变,所以如果遍历时添加和删除元素,会导致 modCount != expectedModCount ,接着会抛出异常
    在这里插入图片描述
    在这里插入图片描述

3.我们在debug中用Evaluate 工具给list添加一个元素”E“, 此时继续执行上次遍历,问你们可以发现list的size已经变成了5 ,modCount也加了1,变成了5;但是此时expectedModCount还是遍历时赋的值,还是4,所以便会抛出异常.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Fail-Safe(安全失败)

java代码

   @Test
    public void testFailSafe() {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        for (String str : list) {
            System.out.println(str);
        }
        ListIterator<String> iterator = list.listIterator();

        System.out.println(list);
    }

1.第一次进入到for循环中,可以看到数据组的长度为4 在这里插入图片描述
2. CopyeOnWriteArrayList中的 foreach遍历真正执行次对象COWIterator:这里有个新的Object[] snapshot对象,看名字就知道是list的快照。 当我们执行遍历时会将snapshot的引用指向 list的array对象,然后用snapshot进行遍历。(看下图snapshot对象的引用地址和第一步的array一样,都是728)(这种情况下如果遍历时并发加入新的元素,是打印不出来新加入的元素的)
在这里插入图片描述
3.这一步用debug中的Evaluate 工具给list添加一个元素”E“,可以看到这里的list中的array又是个新的对象了 (见下图:现在地址是752,第一步中的是728)。 所以安全失败其实就是每次·add()·都会复制出来一个新的数组对象,然后遍历还是用的老的对象,所以遍历时即使你添加了新的元素,也不会把这个元素打印出来
在这里插入图片描述
//add()方法里的源码,可以看到每次add都会创建了一个新的对象
在这里插入图片描述
此时的cursor为2,说明是第二次进入遍历,但是COWIterator 中的 snapshot对象的地址还是728.
在这里插入图片描述

小tip,degbug时集合类展示成对象,而不仅仅是元素

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值