结合ArrayList源码看fast-fail机制

fast-fail机制是一种错误检测机制,在遍历元素时很常见。虽然很基础,但是突然被问起来,靠我的三秒记忆还真不容易说清楚,所以来记录下,方便回忆和复习~
文章内出现的源码来自jdk1.8版本

什么是fast-fail

举个生活中的例子,在上高中的时后,我们每天早上跑操的时候需要统计出勤的人数,我会负责去数今天到了多少人。

报数!        1      2      3      4
 /         /      /      /      /
( `д´)    ​(`ヮ´) ​(`ヮ´) ​(`ヮ´) ​(`ヮ´)

但是!有些不想跑操的同学会在统计完人数后,悄悄溜走。导致实际上出勤人数和统计的预期人数不符合

报告出勤4人  这不是只有三人吗                         报完数溜啰
 /         /                                        /
( `д´)( ´_ゝ`)       (`ヮ´ )σ`∀´) ゚∀゚)σ          ᕕ( ᐛ )ᕗ

而fast-fail机制,就是一种针对这种情况的错误机制。将同学们看作集合里的元素,统计人数的我就是迭代器,当迭代器发现集合的结构发生改变时,直接抛出ConcurrentModificationException异常

出现场景

fast-fail在单线程和多线程都会出现。只要迭代器遍历着发现集合结构和之前不同了,都会报错:

public class FastFailDemo {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        Iterator iterator = list.iterator(); // 获取迭代器
        
        while (iterator.hasNext()) {
            // add和remove修改了原有结构,会导致报错
            list.add(7);
            list.remove(2);
            // set虽然修改了元素,但是结构未修改,不会导致报错
            list.set(0, 7);
            System.err.println(iterator.next()); // 在这一行报错 java.util.ConcurrentModificationException
        }
    }
}

在有subList的情况下,如果对原集合元素进行了增加或删除,且又对subList进行了遍历或增删操作,也会抛出异常:

public class FastFailDemo {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        Iterator<Integer> iterator = list.iterator();

        List<Integer> subList = list.subList(0, 3);
        System.out.println(subList); // 正常遍历[1, 2, 3]
        subList.add(7);
        System.out.println(subList); // 报错 java.util.ConcurrentModificationException
    }
}

源码解读

在这里插入图片描述

1. 什么是modCount

ArrayList继承的AbstractList里有一个关键的成员变量modCount记录了集合结构被修改的次数。源码中对modCount描述为:
modCount为表示列表结构修改的次数。结构修改是指集合大小的变化,避免改变使得在迭代中产生不正确的结果。该值在迭代器中使用到,如果该值的变化不符合预期,迭代器会在调用next remove previous set add方法时抛出异常ConcurrentModificationException
在这里插入图片描述

2. 调用ArrayList的方法修改List结构

然后查看会对列表结构产生修改的方法:add
在这里插入图片描述
在第一行,就是增加modCount的方法,然后才是真正的把新元素插入集合,转跳到具体的修改部分:
在这里插入图片描述
总结下add方法的大致流程是:

  1. 计算增加元素后的大小(最少需要多大的容量)
  2. 增加修改次数modCount++
  3. 容量若不够需要扩容(原容量的1.5倍,不是这个文章的重点就不细说了)
  4. 将新元素加入集合

3. 迭代器遍历抛出异常

然后再看报错的迭代器部分:
迭代器由ArrayList的内部类Itr实现,在构造迭代器时,将List现有的modCount赋值给迭代器的属性。
在这里插入图片描述
在next方法的第一步就是先校验:集合结构是否被修改过
在这里插入图片描述
由于创建迭代器后,又对list进行了两次add操作,所以modCount != expectedModCount返回true,导致报错。
在这里插入图片描述

4. 使用迭代器改变List结构,不抛出异常

迭代器中存在方法Iterator#remove,这个方法用来删除元素不会报错,因为迭代器的删除不会修改modCount,检查时,modCount != expectedModCount返回false
在这里插入图片描述

5. 修改原集合,导致subList遍历报错

子列也是通过内部类,初始属性modCount取现原集合modCount实现的。
在这里插入图片描述
在执行list.add之前,subsList能正常遍历。在执行list.add之后,原list的modCount值增加为5,而子类的modCount没有增加,保持为4。两值不等抛出异常。

在这里插入图片描述

6. subList修改集合,不抛出异常

在调用ArrayList.SubList#add方法时:

  1. 先检查是否超出了下标范围
  2. 再检查了现在modCount是否符合预期
  3. 调用了ArrayList#add方法,此时原list的modCount增加1
  4. 然后将新的modCount赋值给subList的modCount,保持了subList和原list的modCount相等
    在这里插入图片描述
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值