java中的fail-fast(快速失败)和fail-safe(安全失败)

快速失败(fail—fast)

在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。
原理:假设有两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。在使用迭代器遍历时,AbstractList类的内部类Itr中定义了变量expectedModCount,初始值为集合的modCount值。在线程A在遍历list过程中,线程启动,同时线程B增加一个元素,这时modCount的值发生改变(modCount = N + 1)。 线程A继续遍历执行next方法,checkForComodification方法发现expectedModCount = N , 而modCount = N + 1,两者不等,这时就抛出ConcurrentModificationException 异常,从而产生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。
HashMap发生fail-fast:

     public static void main(String[] args) {
           Map<String, String> map = new HashMap<>();
           for (int i = 0 ; i < 10 ; i ++ ) {
                map.put(i+"", i+"");
           }
           Iterator<Entry<String, String>> it = map.entrySet().iterator();
           int i = 0;
           while (it.hasNext()) {
                if (i == 3) {
                     map.remove(3+"");
                }
                Entry<String, String> entry = it.next();
                System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
                i++;
        }
     }

该段代码定义了一个hashmap对象并存放了10个键值对,在迭代遍历过程中,使用map的remove方法移除了一个元素,导致抛出了 ConcurrentModificationException异常
多线程环境下

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();
     }
}

启动两个线程,分别对其中一个对list进行迭代,另一个在线程1的迭代过程中去remove一个元素,结果也是抛出了java.util.ConcurrentModificationException
总结

安全失败(fail—safe)

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但是需要复制集合,产生大量的无效对象,内存消耗大,同时迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值