Java 集合框架04-fail-fast总结

简介: 上一篇我们介绍了ArrayList的相关源码,这篇我们将了解一下fail-fast机制的相关知识

上一篇我们介绍了ArrayList的相关源码,这篇我们将了解一下fail-fast机制的相关知识


1.fail-fast的简介

2.fail-fast的相关示例

3.fail-fast的解决办法

4.fail-fast的原理

解决fail-fast的原理


fail-fast的简介


fail-fast机制是java 集合的一种错误机制。 当多个线程对同一个集合的内容进行操作时,就会产生fail-fast事件。

例如:当线程A通过iterator迭代器(或者 For和RandomAccess)来访问集合时,线程B对集合的内容进行了修改,则线程A在访问集合时就会抛出ConcurrentModificationException异常,产生fail-fast事件。


fail-fast的相关示例

package com.jay.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
 * Created by xiang.wei on 2018/3/4
 *
 * @author xiang.wei
 */
public class FailFastTest {
    /**
     *
     */
    private static List<String> globalList = new ArrayList<String>();
    /**
     *  线程one,two,three同时启动,线程one向globalList中添加元素0,1,2,3,4,5,然后遍历打印
     *  线程two 与此同时也向globalList中添加元素10,11,12,13,14,15,然后遍历打印,
     *  线程three 同时也在移除globalList中元素0,1,2,3,4,5,然后遍历打印
     *
     */
    public static void main(String[] args) {
        new ThreadOne().start();
        new ThreadTwo().start();
        new ThreadThree().start();
    }
    /**
     * 向globalList中添加元素0,1,2,3,4,5,然后遍历打印
     */
    private static class ThreadOne extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 6; i++) {
                globalList.add(String.valueOf(i));
                printAll(globalList);
            }
        }
    }
    /**
     *  向globalList中添加元素10,11,12,13,14,15,然后遍历打印
     */
    private static class ThreadTwo extends Thread {
        @Override
        public void run() {
            for (int i = 10; i < 16; i++) {
                globalList.add(String.valueOf(i));
                printAll(globalList);
            }
        }
    }
    /**
     * 移除globalList中元素0,1,2,3,4,5,然后遍历打印
     */
    private static class ThreadThree extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 6; i++) {
                globalList.remove(String.valueOf(i));
                printAll(globalList);
            }
        }
    }
    private static void printAll(List<String> testList) {
        Iterator<String> testIter = testList.iterator();
        while (testIter.hasNext()) {
            System.out.println((testIter.next()));
        }
    }
}

26d132c275edf55da962c8e217f977f5_SouthEast.png


fail-fast的解决办法

按照Java api文档的建议,在多线程环境中我们需要选用java.util.concurrent包下的CopyOnWriteArrayList集合,

即将

private static List<String> globalList = new ArrayList<String>();

替换成

private static List<String> globalList = new CopyOnWriteArrayList<String>();

fail-fast的原理

我们此处以ArrayList为例,其Iterator的实现是在AbstractList中

private class Itr implements Iterator<E> {
 int cursor = 0;
 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)
                throw new IllegalStateException();
            checkForComodification();
            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }
      final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }


首先,定义一个实例变量

protected transient int modCount = 0;


当实例化一个迭代器Itr时,会将modCount的值赋给expectedModCount,以后每次迭代时都会检查这两个值是否相等,如果不相等则会抛出ConcurrentModificationException。显然,expectedModCount不会改变,那么哪些操作会修改modCount的值呢?

(ps: 这也解释了为啥iterator为啥可以直接移除当前元素)

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
         //增加modCount的值
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
        public E remove(int index) {
        rangeCheck(index);
         //增加modCount的值
        modCount++;
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
        return oldValue;
    }


从上述源码中我们可以看出 add,remove方法修改modCount的值。


解决fail-fast的原理


我们接着看看CopyOnWriteArrayList是如何解决fail-fast的问题的。

首先CopyOnWriteArrayList的定义

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}


CopyOnWriteArrayList直接实现了List,其iterator的迭代器COWIterator是在自身类实现的。

static final class COWIterator<E> implements ListIterator<E> {
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;
 private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }
        public boolean hasNext() {
            return cursor < snapshot.length;
        }
        public boolean hasPrevious() {
            return cursor > 0;
        }
        @SuppressWarnings("unchecked")
        public E next() {
            if (! hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }


(01) 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。

(02) ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。

(03) ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!


引用

http://www.cnblogs.com/skywang12345/p/3308762.html


相关文章
|
4月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
4月前
|
存储 安全 Java
《数据之美》:Java集合框架全景解析
Java集合框架是数据管理的核心工具,涵盖List、Set、Map等体系,提供丰富接口与实现类,支持高效的数据操作与算法处理。
|
4月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
242 8
|
4月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
143 7
|
5月前
|
Java 大数据 API
Java Stream API:现代集合处理与函数式编程
Java Stream API:现代集合处理与函数式编程
323 100
|
5月前
|
Java API 数据处理
Java Stream API:现代集合处理新方式
Java Stream API:现代集合处理新方式
343 101
|
5月前
|
人工智能 Java 开发者
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
JManus是阿里开源的Java版OpenManus,基于Spring AI Alibaba框架,助力Java开发者便捷应用AI技术。支持多Agent框架、网页配置、MCP协议及PLAN-ACT模式,可集成多模型,适配阿里云百炼平台与本地ollama。提供Docker与源码部署方式,具备无限上下文处理能力,适用于复杂AI场景。当前仍在完善模型配置等功能,欢迎参与开源共建。
2296 58
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
|
5月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
|
5月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
5月前
|
SQL Java 数据库连接
区分iBatis与MyBatis:两个Java数据库框架的比较
总结起来:虽然从技术角度看,iBATIS已经停止更新但仍然可用;然而考虑到长期项目健康度及未来可能需求变化情况下MYBATISS无疑会是一个更佳选择因其具备良好生命周期管理机制同时也因为社区力量背书确保问题修复新特征添加速度快捷有效.
334 12