栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

fail-fast快速失败

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

fail-fast快速失败

简介

fail-fast 机制,即快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出 ConcurrentModificationException异常。fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大努力去抛出,所以这种机制一般仅用于检测bug。

fail-fast出现场景
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class fail_fast {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0 ; i < 10 ; i++ ) {
            list.add(i+"");
        }
        Iterator iterator = list.iterator();//通过集合得到迭代器对象
        while(iterator.hasNext()) {
            String i=(String)iterator.next();
            if (i.equals("3")) {
                list.remove(3);//使用集合对象修改集合元素
            }
            System.out.println(i);
        }
    }
}


由上段程序及其结果可知,在迭代器遍历过程中,如果使用集合对象来改变集合里的元素,则会产生ConcurrentModificationException异常。

fail-fast原理及源码分析

源码涉及的重要变量:

变量名意义
int cursor即将遍历的元素的索引
int lastRet刚刚遍历过的元素的索引,默认值为-1
int expectedModCount预期修改次数,初始值为ArrayList中的modCount

ArrayList中的modCount用于记录集合操作过程中作的修改次数

Iterator其实只是一个接口,具体的实现还是要看具体的集合类中的内部类去实现Iterator并实现相关方法,这里以ArrayList类为例,查看其源码:

public Iterator iterator() {
        return new Itr();
    }

Itr类是ArrayList的内部类,实现了Iterator接口,下面是该类的源码:

    
    private class Itr implements Iterator {
        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];
        }
 
        public void remove() {
            if (lastRet < 0)// 如果没有next()操作就直接remove的话,lastRet保持默认值,-1,会抛异常
                throw new IllegalStateException();
            checkForComodification();
 
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;// 为了防止ConcurrentModificationException异常,手动修复了修改计数的期望值
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
 
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
 
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

上面程序Itr中hasNext()方法解析:

        public boolean hasNext() {
            return cursor != size;
        }

表示当即将遍历的元素的索引等于集合元素个数,则遍历完成。

上面程序Itr中的next()方法解析:

       @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];
        }

我们可以看到next()方法里,在实际访问元素之前,都会先调用checkForComodification()方法,其源码如下

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

当集合实际修改次数不等于预期修改次数则抛异常,但是expectedModCount初始值默认等于modCount,expectedModCount在整个迭代过程除了一开始赋予初始值modCount外,并没有再发生改变,所以在迭代过程中modCount发生了改变(ArrayList进行add,remove,clear等涉及到修改集合中的元素个数的操作时,modCount就会发生改变(modCount ++))

避免fail-fast

在使用迭代器对象的remove()方法删除上次next()方法返回的元素时,并不会发生异常

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class fail_fast {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        for (int i = 0 ; i < 10 ; i++ ) {
            list.add(i+"");
        }
        Iterator iterator = list.iterator();//通过集合得到迭代器对象
        while(iterator.hasNext()) {
            String i=(String)iterator.next();
            if (i.equals("3")) {
//                list.remove(3);
                iterator.remove();//上次iterator.next()返回的应该是3
                System.out.println(list);
            }
//            System.out.println(i);
        }
    }
}

ArrayList中迭代器的remove方法的源码:

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
 
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

为了防止ConcurrentModificationException异常,手动修复了修改计数的期望值,所以无论modCount如何改变都不会产生异常。

产生异常的checkForComodification()方法,其源码如下:

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/571758.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号