zoukankan      html  css  js  c++  java
  • fail-fast(快速失败)机制和fail-safe(安全失败)机制的介绍和区别

    fail-fast和fail-safe的区别: 

    fail-safe允许在遍历的过程中对容器中的数据进行修改,而fail-fast则不允许。

    fail-fast ( 快速失败 )
    fail-fast:直接在容器上进行遍历,在遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常导致遍历失败。java.util包下的集合类都是快速失败机制的, 常见的的使用fail-fast方式遍历的容器有HashMap和ArrayList等。

    在使用迭代器遍历一个集合对象时,比如增强for,如果遍历过程中对集合对象的内容进行了修改(增删改),会抛出ConcurrentModificationException 异常.

    fail-fast的出现场景
    在我们常见的java集合中就可能出现fail-fast机制,比如ArrayList,HashMap。在多线程和单线程环境下都有可能出现快速失败。

    1、单线程环境下的fail-fast:
    ArrayList发生fail-fast例子:

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0 ; i < 10 ; i++ ) {
            list.add(i + "");
        }
        Iterator<String> iterator = list.iterator();
        int i = 0 ;
        while(iterator.hasNext()) {
            if (i == 3) {
                 list.remove(3);
            }
            System.out.println(iterator.next());
            i ++;
        }
    }

    该段代码定义了一个Arraylist集合,并使用迭代器遍历,在遍历过程中,刻意在某一步迭代中remove一个元素,这个时候,就会发生fail-fast

    2、多线程环境下:

    public class FailFastTest {
         public static List<String> list = new ArrayList<>();
     
         private static class MyThread1 extends Thread {
               @Override
               public void run() {
                    Iterator<String> iterator = list.iterator();
                    while(iterator.hasNext()) {
                         String s = iterator.next();
                         System.out.println(this.getName() + ":" + s);
                         try {
                           Thread.sleep(1000);
                         } catch (InterruptedException e) {
                            e.printStackTrace();
                         }
                    }
                    super.run();
               }
         }
     
         private static class MyThread2 extends Thread {
               int i = 0;
               @Override
               public void run() {
                    while (i < 10) {
                         System.out.println("thread2:" + i);
                         if (i == 2) {
                             list.remove(i);
                         }
                         try {
                             Thread.sleep(1000);
                         } catch (InterruptedException e) {
                             e.printStackTrace();
                         }
                         i ++;
                    }
               }
         }
     
         public static void main(String[] args) {
               for(int i = 0 ; i < 10;i++){
                   list.add(i+"");
               }
               MyThread1 thread1 = new MyThread1();
               MyThread2 thread2 = new MyThread2();
               thread1.setName("thread1");
               thread2.setName("thread2");
               thread1.start();
               thread2.start();
         }
    }

    fail-fast的原理:

    fail-fast是如何抛出ConcurrentModificationException异常的,又是在什么情况下才会抛出?
    我们知道,对于集合如list,map类,我们都可以通过迭代器来遍历,而Iterator其实只是一个接口,具体的实现还是要看具体的集合类中的内部类去实现Iterator并实现相关方法。这里我们就以ArrayList类为例。在ArrayList中,当调用list.iterator()时,其源码是: 

    public Iterator<E> iterator() {
            return new Itr();
    
    }

    避免fail-fast的方法:

    方法1
    在单线程的遍历过程中,如果要进行remove操作,可以调用迭代器的remove方法而不是集合类的remove方法

    方法2

    使用fail-safe机制,使用java并发包(java.util.concurrent)中的CopyOnWriterArrayList类来代替ArrayList,使用 ConcurrentHashMap来代替hashMap。

    fail-safe ( 安全失败 )
    fail-safe:这种遍历基于容器的一个克隆。因此,对容器内容的修改不影响遍历。java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改。常见的的使用fail-safe方式遍历的容器有ConcerrentHashMap和CopyOnWriteArrayList等。

    原理:

    采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。

    缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

  • 相关阅读:
    进入用友通:提示"由于文件不可访问,内存磁盘空间不足无法打开ufsystem数据库"...
    HDOJ 1069 Monkey and Banana
    HDOJ 1087 Super Jumping! Jumping! Jumping!
    HDOJ 1209 Clock
    CodeForces Round #185 (Div. 2)A,B,C
    HDOJ 1465 不容易系列之一
    HDOJ 1114 PiggyBank
    HDOJ 1280 前m大的数
    HDOJ 1495 非常可乐
    HDOJ 1284 钱币兑换问题
  • 原文地址:https://www.cnblogs.com/u013533289/p/11637885.html
Copyright © 2011-2022 走看看