iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >fail-fast机制
  • 281
分享到

fail-fast机制

java教程java 2018-02-01 14:02:29 281人浏览 无得
摘要

在jdk的Collection中我们时常会看到类似于这样的话:例如,ArrayList:注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽 最大努力抛出 Concurre

jdk的Collection中我们时常会看到类似于这样的话:

例如,ArrayList:

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽
最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭
代器的快速失败行为应该仅用于检测 bug。

HashMap中:

注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大
努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应
该仅用于检测程序错误。

在这两段话中反复地提到”快速失败”。那么何为”快速失败”机制呢?

“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。

一、fail-fast示例

public class FailFastTest {
    private static List list = new ArrayList<>();
 
    
    private static class threadOne extends Thread{
        public void run() {
            Iterator iterator = list.iterator();
            while(iterator.hasNext()){
                int i = iterator.next();
                System.out.println("ThreadOne 遍历:" + i);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
    
    private static class threadTwo extends Thread{
        public void run(){
            int i = 0 ;
            while(i < 6){
                System.out.println("ThreadTwo run:" + i);
                if(i == 3){
                    list.remove(i);
                }
                i++;
            }
        }
    }
 
    public static void main(String[] args) {
        for(int i = 0 ; i < 10;i++){
            list.add(i);
        }
        new threadOne().start();
        new threadTwo().start();
    }
}

运行结果:

ThreadOne 遍历:0
ThreadTwo run:0
ThreadTwo run:1
ThreadTwo run:2
ThreadTwo run:3
ThreadTwo run:4
ThreadTwo run:5
Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at test.ArrayListTest$threadOne.run(ArrayListTest.java:23

二、fail-fast产生原因

通过上面的示例和讲解,我初步知道fail-fast产生的原因就在于程序在对 collection 进行迭代时,某个线程对该 collection 在结构上对其做了修改,这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。

要了解fail-fast机制,我们首先要对ConcurrentModificationException 异常有所了解。当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常。同时需要注意的是,该异常不会始终指出对象已经由不同线程并发修改,如果单线程违反了规则,同样也有可能会抛出改异常。

诚然,迭代器的快速失败行为无法得到保证,它不能保证一定会出现该错误,但是快速失败操作会尽最大努力抛出ConcurrentModificationException异常,所以因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。下面我将以ArrayList为例进一步分析fail-fast产生的原因。

从前面我们知道fail-fast是在操作迭代器时产生的。现在我们来看看ArrayList中迭代器的源代码:

private class Itr implements Iterator {
        int cursor;
        int lastRet = -1;
        int expectedModCount = ArrayList.this.modCount;
 
        public boolean hasNext() {
            return (this.cursor != ArrayList.this.size);
        }
 
        public E next() {
            checkForComodification();
            
        }
 
        public void remove() {
            if (this.lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
            
        }
 
        final void checkForComodification() {
            if (ArrayList.this.modCount == this.expectedModCount)
                return;
            throw new ConcurrentModificationException();
        }
    }

从上面的源代码我们可以看出,迭代器在调用next()、remove()方法时都是调用checkForComodification()方法,该方法主要就是检测modCount == expectedModCount ? 若不等则抛出ConcurrentModificationException 异常,从而产生fail-fast机制。所以要弄清楚为什么会产生fail-fast机制我们就必须要用弄明白为什么modCount != expectedModCount ,他们的值在什么时候发生改变的。

expectedModCount 是在Itr中定义的:int expectedModCount = ArrayList.this.modCount;所以他的值是不可能会修改的,所以会变的就是modCount。modCount是在 AbstractList 中定义的,为全局变量:

 protected transient int modCount = 0;

那么他什么时候因为什么原因而发生改变呢?请看ArrayList的源码

public boolean add(E paramE) {
    ensureCapacityInternal(this.size + 1);
    
}

private void ensureCapacityInternal(int paramInt) {
    if (this.elementData == EMPTY_ELEMENTDATA)
        paramInt = Math.max(10, paramInt);
    ensureExplicitCapacity(paramInt);
}

private void ensureExplicitCapacity(int paramInt) {
    this.modCount += 1;    //修改modCount
    
}

public boolean remove(Object paramObject) {
    int i;
    if (paramObject == null)
        for (i = 0; i < this.size; ++i) {
            if (this.elementData[i] != null)
                continue;
            fastRemove(i);
            return true;
        }
    else
        for (i = 0; i < this.size; ++i) {
            if (!(paramObject.equals(this.elementData[i])))
                continue;
            fastRemove(i);
            return true;
        }
    return false;
}

private void fastRemove(int paramInt) {
    this.modCount += 1;   //修改modCount
    
}

public void clear() {
    this.modCount += 1;    //修改modCount
    
}

从上面的源代码我们可以看出,ArrayList中无论add、remove、clear方法只要是涉及了改变ArrayList元素的个数的方法都会导致modCount的改变。所以我们这里可以初步判断由于expectedModCount 得值与modCount的改变不同步,导致两者之间不等从而产生fail-fast机制。知道产生fail-fast产生的根本原因了,我们可以有如下场景:

有两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。线程A在遍历list过程的某个时候(此时expectedModCount = modCount=N),线程启动,同时线程B增加一个元素,这是modCount的值发生改变(modCount + 1 = N + 1)。线程A继续遍历执行next方法时,通告checkForComodification方法发现expectedModCount = N ,而modCount = N + 1,两者不等,这时就抛出ConcurrentModificationException 异常,从而产生fail-fast机制。

所以,直到这里我们已经完全了解了fail-fast产生的根本原因了。知道了原因就好找解决办法了。

三、fail-fast解决办法

通过前面的实例、源码分析,我想各位已经基本了解了fail-fast的机制,下面我就产生的原因提出解决方案。这里有两种解决方案:

方案一: 在遍历过程中所有涉及到改变modCount值得地方全部加上synchronized或者直接使用Collections.synchronizedList,这样就可以解决。但是不推荐,因为增删造成的同步可能会阻塞遍历操作。

方案二: 使用CopyOnWriteArrayList来替换ArrayList。推荐使用该方案。

--结束END--

本文标题: fail-fast机制

本文链接: https://www.lsjlt.com/news/4227.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 老生常谈java中的fail-fast机制
    在JDK的Collection中我们时常会看到类似于这样的话:例如,ArrayList:注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 Concurren...
    99+
    2023-05-31
    java fail fast
  • Java的Iterator,fail-fast机制与比较器怎么使用
    本篇内容主要讲解“Java的Iterator,fail-fast机制与比较器怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java的Iterator,fail-fast机制与比较器怎么使...
    99+
    2023-06-02
  • Java集合详解3:一文读懂Iterator,fail-fast机制与比较器
    《Java集合详解系列》是我在完成夯实Java基础篇的系列博客后准备开始写的新系列。这些文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看https://github.com/h3pl/Java-Tut...
    99+
    2023-06-02
  • 【字节面试】Fail-fast知识点相关知识点
    字节面试,问到的一个小知识点,这里做一下总结,其实小编之前有一篇文章,已经对此有过涉及,不过这里知识专项针对于问题,把这个知识点拎出来说一下。 1.问题 什么是Fail-fast机制? Hash...
    99+
    2023-08-31
    面试 java 字节 fail-fast Enumeration
  • Fast Recovery Area空间用满后的自动清理机制
    使用Fast Recovery Area最大的好处在于oracle能够对于其中存放的备份恢复相关的对象进行自动管理,特别是在Fast Recovery Area空间利用率达到100%时能够按照保留策略对其中...
    99+
    2022-11-30
    area fast recovery
  • Win7开机自检出现Floopy disk fail错误解决方法
    最近有一朋友在开启Win7系统的时候出现自检,报错Floopy disk fail,Press F1 to continue,DEL to Enter...
    99+
    2023-06-13
    Win7 开机自检 Floopy disk fail错误 解决 错误 fail 方法
  • SpringBoot集成Nacos控制台报:Server check fail, please check server xxx ,port 9848 is available
    问题: 2023-03-06 00:28:13.284 ERROR 329700 --- [t.remote.worker] c.a.n.c.remote.client.grpc.GrpcClient :99 - Server che...
    99+
    2023-09-15
    spring boot spring cloud
  • python 锁机制
    锁(LOCK)当有两个或跟多个线程或进程需要操作一个变量或进程时,会出现意想不到的结果,这是因为线程或进程时迸发进行的,对同意变量或文件操作时,会出现同时对其操作,从到导致逻辑错误。#!/bin/usr/env python #coding...
    99+
    2023-01-31
    机制 python
  • 浅谈Android IPC机制之Binder的工作机制
    目录进程和线程的关系跨进程的种类Serializable,Parcelable接口Binder进程和线程的关系 按照操作系统中的描述,线程是CPU调度的最小单位,同时线程也是一种有限...
    99+
    2024-04-02
  • kafka的重试机制和ack机制是什么
    Kafka的重试机制是指在消息发送过程中,如果发送失败或者出现异常,Kafka会自动尝试重新发送消息。重试机制的目的是确保消息能够成...
    99+
    2023-10-26
    kafka
  • python import 机制
    Python 环境初始化过程中就会将sys module加载到内存中,但是为了local空间更干净,需要用户亲自导入,通知python.module对象实际上是一个dict在维护着,hello.__dict__打印出属性和属性值...
    99+
    2023-01-31
    机制 python import
  • Android View 绘制机制的详解
    View 绘制机制一、 View 树的绘图流程当 Activity 接收到焦点的时候,它会被请求绘制布局,该请求由 Android framework 处理.绘制是从根节点开始,对布局树进行 measure 和 draw。整个 View 树...
    99+
    2023-05-31
    android view 绘制
  • Django 惰性机制
    惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行sql,为了测试,我们加上 sql 日志。 ...
    99+
    2023-01-31
    惰性 机制 Django
  • Java 反射机制
    简介: Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个...
    99+
    2024-04-02
  • feign的Fallback机制
    对接口使用@FeignClient后声明feign客户端后,可以使用属性fallback指定异常处理类,这个类必须实现@FeignClient作用的接口,且被注入到容器中。 @FeignClient(name = "service-prov...
    99+
    2023-08-16
    java spring cloud feign
  • 详解php内存管理机制与垃圾回收机制
    目录一、内存管理机制二、垃圾回收机制一、内存管理机制 先看一段代码: <?php //内存管理机制 var_dump(memory_get_usage());//获...
    99+
    2024-04-02
  • Couchbase主从复制机制是什么
    Couchbase的主从复制机制是一种用于在分布式数据库集群中实现数据复制和数据同步的技术。在Couchbase中,主从复制机制由两...
    99+
    2024-04-09
    Couchbase
  • Java RMI机制讲解
    Java RMI Java RMI之HelloWorld篇 Java RMI 指的是远程方法调用 (Remote Method Invocation)。它是一种机制,能够让在某个 J...
    99+
    2024-04-02
  • 浅谈Java锁机制
    目录1、悲观锁和乐观锁2、悲观锁应用3、乐观锁应用4、CAS5、手写一个自旋锁1、悲观锁和乐观锁 我们可以将锁大体分为两类: 悲观锁 乐观锁 顾名思义,悲观锁总是...
    99+
    2024-04-02
  • Java CAS机制详解
    目录一、什么是CAS什么是CAS机制为何CAS如此优秀CAS为什么要和volitile配合使用二、Java中的Atomic原子操作包三、类AtomicInteger四、Unsafe类...
    99+
    2023-01-28
    Java CAS Java CAS机制
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作