首页 > 编程语言 >java中fail-fast 和 fail-safe的区别

java中fail-fast 和 fail-safe的区别

时间:2023-05-29 13:02:56浏览次数:49  
标签:java 迭代 iterator safe util 修改 fail



 

在我们详细讨论这两种机制的区别之前,首先得先了解并发修改。

1.什么是并发修改?

当一个或多个线程正在遍历一个集合Collection,此时另一个线程修改了这个集合的内容(添加,删除或者修改)。这就是并发修改。

 

2.什么是 fail-fast 机制?

fail-fast机制在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception。

fail-fast会在以下两种情况下抛出ConcurrentModificationException

(1)单线程环境

集合被创建后,在使用Iterator遍历它的过程中修改了结构。

注意Iterator的 remove()方法会让expectModcount和modcount 相等,所以是不会抛出这个异常。

(2)多线程环境

当一个线程在遍历这个集合,而另一个线程对这个集合的结构进行了修改。

 

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

 

3. fail-fast机制是如何检测的?

迭代器在遍历过程中是直接访问内部数据的,因此内部的数据在遍历的过程中无法被修改。为了保证不被修改,迭代器内部维护了一个标记 “mode” ,当集合结构改变(添加删除或者修改),标记"mode"会被修改,而迭代器每次的hasNext()和next()方法都会检查该"mode"是否被改变,当检测到被修改时,抛出Concurrent Modification Exception。

下面看看ArrayList迭代器部分的源码:


1. private class Itr implements Iterator<E> {  
2. int cursor;  
3. int lastRet = -1;  
4. int expectedModCount = ArrayList.this.modCount;  
5.   
6. public boolean hasNext() {  
7. return (this.cursor != ArrayList.this.size);  
8.         }  
9.   
10. public E next() {  
11.             checkForComodification();  
12. /** 省略此处代码 */  
13.         }  
14.   
15. public void remove() {  
16. if (this.lastRet < 0)  
17. throw new IllegalStateException();  
18.             checkForComodification();  
19.           try {
20.               ArrayList.this.remove(lastRet);
21.               cursor = lastRet;
22.               lastRet = -1;
23. expectedModCount = modCount;
24.           } catch (IndexOutOfBoundsException ex) {
25.               throw new ConcurrentModificationException();
26.           }
27.         }  
28.   
29. final void checkForComodification() {  
30. if (ArrayList.this.modCount == this.expectedModCount)  
31. return;  
32. throw new ConcurrentModificationException();  
33.         }  
34.     }



可以看到它的标记“mode”为 expectedModeCount

 

4. fail-safe机制

fail-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException

fail-safe机制有两个问题

(1)需要复制集合,产生大量的无效对象,开销大

(2)无法保证读取的数据是目前原始数据结构中的数据。

 

5.fail-fast 和 fail-safe的例子

1. import java.util.HashMap;  
2. import java.util.Iterator;  
3. import java.util.Map;  
4.   
5. public class FailFastExample  
6. {  
7.       
8.       
9. public static void main(String[] args)  
10.     {  
11. new HashMap<String,String>();  
12. "Apple", "iPhone");  
13. "HTC", "HTC one");  
14. "Samsung","S5");  
15.           
16.         Iterator iterator = premiumPhone.keySet().iterator();  
17.           
18. while (iterator.hasNext())  
19.         {  
20.             System.out.println(premiumPhone.get(iterator.next()));  
21. "Sony", "Xperia Z");  
22.         }  
23.           
24.     }  
25.       
26. }



输出

 

iPhone
Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
        at java.util.HashMap$KeyIterator.next(Unknown Source)
        at FailFastExample.main(FailFastExample.java:20)

1. import java.util.concurrent.ConcurrentHashMap;  
2. import java.util.Iterator;  
3.   
4.   
5. public class FailSafeExample  
6. {  
7.       
8.       
9. public static void main(String[] args)  
10.     {  
11.         ConcurrentHashMap<String,String> premiumPhone =   
12. new ConcurrentHashMap<String,String>();  
13. "Apple", "iPhone");  
14. "HTC", "HTC one");  
15. "Samsung","S5");  
16.           
17.         Iterator iterator = premiumPhone.keySet().iterator();  
18.           
19. while (iterator.hasNext())  
20.         {  
21.             System.out.println(premiumPhone.get(iterator.next()));  
22. "Sony", "Xperia Z");  
23.         }  
24.           
25.     }  
26.       
27. }



输出 



S5 HTC one iPhone



  

6. fail-fast和 fail-safe 的区别

 

java中fail-fast 和 fail-safe的区别_java

 

 

标签:java,迭代,iterator,safe,util,修改,fail
From: https://blog.51cto.com/u_16131764/6370125

相关文章

  • java socket
         对于JavaSocket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了。首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个......
  • Java中的堆和栈的区别
    当一个人开始学习Java或者其他编程语言的时候,会接触到堆和栈,由于一开始没有明确清晰的说明解释,很多人会产生很多疑问,什么是堆,什么是栈,堆和栈有什么区别?更糟糕的是,Java中存在栈这样一个后进先出(LastInFirstOut)的顺序的数据结构,这就是java.util.Stack。这种情况下,不免让很多人更加......
  • java二进制运算
    //对于原码,反码,补码而言,需要注意以下几点://(1) 二进制的最高位是符号位,0表示正数,1表示负数;//(2) 正数的原码,反码,补码都一样;//(3) 负数的原码=对应正数的二进制原码,最高位设为1;//(4) 负数的反码=它的原码符号位不变,其他位取反;//(5) 负数的补码=它的......
  • 压栈思想计算Java运算表达式
         栈的规则是先进后出。利用压栈的思想来计算四则运算表达式是这样的:我们给定两个栈,一个用来存放数字、一个用来存放对应的操作符。假定我们有一个给定的四则运算表达式a+b+c/d*(e+f)-d*a,那我们先把这个表达式拆分成一个个的数字或者是运算符、或者就是括号了。然后我们......
  • java IO
    packagecom.jaeson.javastudy;importjava.io.*;importjava.util.zip.*;publicclassIOTest{ //跨平台的文件分割符 staticvoidseparator(){ System.out.println(File.separator); //\System.out.println(File.pathSeparator); //; } //创建文件,创......
  • Java内存溢出和内存泄露
    一、为什么要了解内存泄露和内存溢出? 1、内存泄露一般是代码设计存在缺陷导致的,通过了解内存泄露的场景,可以避免不必要的内存溢出和提高自己的代码编写水平;2、通过了解内存溢出的几种常见情况,可以在出现内存溢出的时候快速的定位问题的位置,缩短解决故障的时间。 二、基本概念 内......
  • Java 内存模型及GC原理
    一个优秀Java程序员,必须了解Java内存模型、GC工作原理,以及如何优化GC的性能、与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统、实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序的性能。本文将从JVM内存模型、GC工作原理,以及GC的几个关键问题进行探讨,从G......
  • 理解Java中的ThreadLocal
    提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和认识,希望让大家理解ThreadLocal更加透彻一些。ThreadLocal是什么 ThreadLocal是一个关于创建线程局部变量的类。通......
  • java虚拟机总结
     类型的生命周期:java虚拟机通过装载、连接和初始化一个java类型,使该类型可以被正在运行的java程序所使用。装载:是把二进制形式的java类型读入java虚拟机中。连接:是把读入的二进制形式的类型数据合并到虚拟机的运行时状态中去。连接分三个子步骤(验证、准      备和解析......
  • mysql、sqlserver、oracle分页,java分页统一接口实现
    定义:pageStart起始页,pageEnd终止页,pageSize页面容量oracle分页:rownum numfrom(实际传的SQL)where rownum<=pageEnd)wherenum>=pageStartsqlServer分页:           select*from(select top 页面容量from(select top字段Adesc)astemptable2orderb......