Java集合中快速失败机制详解

2 篇文章 0 订阅

前言

  • 以前在看JDK集合源码时,会发现其中有快速失败和失败安全机制的应用实现(以前刚开始看时不知道这个东东是什么意思,后来了解多了才知道这个-_-)
  • 快速失败和失败安全机制其实是一种设计思想,其应用场景不只在Java集合类,很多开源框架里也有提现,Dubbo的集群容错机制,其中除了有快速失败和失败安全机制外,还有失败自动切换、失败自动回复、并行调用多个服务机制。
  • 本人对dubbo框架了解不多,本次只讨论JDK集合内快速失败和失败安全机制的实现。

快速失败机制

  • 首先看一到面试题:
public static void main (String[] args) {
   ArrayList<Integer> list = new ArrayList<>();
   list.add(1);
   list.add(2);
   for (Integer num : list) {
       if (2 == num) {
           list.remove(num);
       }
   }
   System.out.println(list);
}
  • 此代码执行结果是什么?
  • 很明显这题其实不难,不会打印,直接抛异常:java.util.ConcurrentModificationException

1.png

  • 根据异常信息,在ArrayList.java:909行抛异常,点进去看会发现:
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
  • 当满足条件modCount != expectedModCount,即会抛出ConcurrentModificationException。
  • 那就看看这两个参数是什么意思。 并且在哪种情况下会满足modCount != expectedModCount。

modCount

  • 看一下modCount字段的备注:

2.png

  • 本人英语不好没法整段翻译,着重看图中圈起来的位置说明其fail-fast(快速失败),并且在add和remove方法内对其自增。

3.png

4.png

  • 上图分别是add方法和remove方法里对modCount的自增操作

expectedModCount

  • 在集合中的一个内部类Itr中,有一个属性expectedModCount,根据字面翻译:预期修改次数

5.png

  • 初始化Itr类对象时会第一次赋值:
int expectedModCount = modCount;

6.png

  • 在Itr对象里调用remove方法是,会再次进行赋值操作。

modCount != expectedModCount

  • 首先去除Java语法糖机制,看当前类加载后的class文件

7.png

  • 在当前foreach循环删除的场景里,foreach最后会被编译成使用Iterator对象。
  • 这里直接调用集合的remove方法,那么会导致modCount,然而Iterator对象里的expectedModCount属性没有更新,所以在使用Iterator对象获取元素时的前置检测不通过,抛出异常:ConcurrentModificationException。
  • 如果使用Iterator对象的remove方法进行删除元素,会对expectedModCount进行赋值操作,以确保modCount == expectedModCount。并发时需要对Iterator对象进行加锁
注:上诉前置检测抛异常原理其实就是快速失败机制的体现,在迭代器访问集合元素时,如果集合内的元素有改动操作,则立即抛出异常。

失败安全机制

  • 和快速失败不同,安全失败是调用过程出现了异常,则是不抛出只记录或打印,然后继续执行后续流程。
  • 在java.util.concurrent的并发集合内都是使用安全失败机制,在遍历集合时不是在原集合上进行遍历,而是直接复制一份原集合内容,在复制的集合内容上进行遍历操作
  • 使用复制原集合内容方式能避免快速失败机制,但是会增加内存占用,并且一致性无法保证,只能保证数据的最终一致性。
  • java.util.concurrent下并发集合可在多线程中并发执行,因为是不会操作原集合内容,所以执行过程中互不影响,都会执行后续流程。
public static void main (String[] args) {
    CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
    list.add(1);
    list.add(2);
    final Iterator<Integer> iterator = list.iterator();
    while (iterator.hasNext()) {
        Integer next = iterator.next();
        if (2 == next) {
            list.remove(next);
        }
    }
    System.out.println(list);
}
  • 这里会正常打印结果

最后

  • 如果上述表达有任何问题,欢迎指出!
  • 最后虚心学习,共同进步 -_-
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值