快速失败Vs安全失败 我会带着你远行 2022-05-15 23:04 190阅读 0赞 简介: 当错误发生时,如果系统立即关闭,即是快速失败,系统不会继续运行。运行中发生错误,它会立即停止操作,错误也会立即暴露。而安全失败系统在错误发生时不会停止运行。它们隐蔽错误,继续运行,而不会暴露错误。这两种模式,孰优孰优,是系统设计中常讨论的话题,在此,我们只讨论java中的快速失败和安全失败迭代器。 Java快速失败与安全失败迭代器 : java迭代器提供了遍历集合对象的功能,集合返回的迭代器有快速失败型的也有安全失败型的,快速失败迭代器在迭代时如果集合类被修改,立即抛出ConcurrentModificationException异常,而安全失败迭代器不会抛出异常,因为它是在集合类的克隆对象上操作的。我们来看看快速失败和 安全失败迭代器的具体细节。 java快速失败迭代器 : 大多数集合类返回的快速失败迭代器在遍历时不允许结构性修改(结构性修改指添加,删除和更新一个元素) 当遍历的同时被结构性修改,就会抛出ConcurrentModificationException异常,而当集合是被迭代器自带的方法(如remove())修改时,不会抛出异常。 快速失败迭代器运行原理: 所有的集合类都维护着一个对象数组(Object\[\]),用来存储元素, 快速失败迭代器直接从数组中获取元素,在迭代过程中,总是假定该内部数组不会被修改。为了判断这个集合是否被修改,它们使用名为modCount的内部标识,当集合被修改,该标识也会更新。迭代器每次调用next()方法,都会检查modCount,如果发现modCount被更新,就会抛出ConcurrentModificationException异常。 ArrayList,Vector,HashMap等集合返回的迭代器都是快速失败类型的。 import java.util.ArrayList; import java.util.Iterator; public class FailFastIteratorExample \{ public static void main(String\[\] args) \{ //Creating an ArrayList of integers ArrayList<Integer> list = new ArrayList<Integer>(); //Adding elements to list list.add(1452); list.add(6854); list.add(8741); list.add(6542); list.add(3845); //Getting an Iterator from list Iterator<Integer> it = list.iterator(); while (it.hasNext()) \{ Integer integer = (Integer) it.next(); list.add(8457); //This will throw ConcurrentModificationException \} \} \} Output : Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at java.util.ArrayList$Itr.next(Unknown Source) at pack1.MainClass.main(MainClass.java:32) **Java安全失败迭代器 :** **安全失败迭代器在迭代中被修改,不会抛出任何异常,因为它是在集合的克隆对象迭代的,所以任何对原集合对象的结构性修改都会被迭代器忽略,但是这类迭代器有一些缺点,其一是它不能保证你迭代时获取的是最新数据,因为迭代器创建之后对集合的任何修改都不会在该迭代器中更新,还有一个缺点就是创建克隆对象在时间和内存上都会增加一些负担。** ConcurrentHashMap返回的迭代器是安全失败迭代器: import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; public class FailSafeIteratorExample \{ public static void main(String\[\] args) \{ //Creating a ConcurrentHashMap ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>(); //Adding elements to map map.put("ONE", 1); map.put("TWO", 2); map.put("THREE", 3); map.put("FOUR", 4); //Getting an Iterator from map Iterator<String> it = map.keySet().iterator(); while (it.hasNext()) \{ String key = (String) it.next(); System.out.println(key+" : "+map.get(key)); map.put("FIVE", 5); //This will not be reflected in the Iterator \} \} \} Output : TWO : 2 FOUR : 4 ONE : 1 THREE : 3 <table> <tbody> <tr> <td style="vertical-align:top;">快速失败迭代器</td> <td style="vertical-align:top;">安全失败迭代器</td> </tr> <tr> <td style="vertical-align:top;">在迭代时不允许修改集合</td> <td style="vertical-align:top;">在迭代时允许修改集合</td> </tr> <tr> <td style="vertical-align:top;">迭代时被修改抛出ConcurrentModificationException异常</td> <td style="vertical-align:top;">迭代时集合被修改不抛出异常</td> </tr> <tr> <td style="vertical-align:top;">使用原集合遍历集合元素</td> <td style="vertical-align:top;">使用原集合的副本遍历集合元素</td> </tr> <tr> <td style="vertical-align:top;">迭代器不要求额外的内存</td> <td style="vertical-align:top;">迭代器需要额外的内存克隆集合对象</td> </tr> <tr> <td colspan="1" style="vertical-align:top;">示例:ArrayList, Vector, HashMap</td> <td colspan="1" style="vertical-align:top;">示例:ConcurrentHashMap</td> </tr> </tbody> </table> 译者语: 总体而言是一篇好文章,就是有一个地方和javadoc有出入,即“大多数集合类返回的快速失败迭代器在遍历时不允许结构性修改(结构性修改指添加,删除和更新集合中一个元素)”这一句,而javadoc中对此的解释是“结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。” 以javadoc中的为准。 牛客讨论:[快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?][fail-fast_fail-safe] 但是自己多个线程同时遍历和修改concurrenthashmap时,迭代器和map对象却都能实时的更改,貌似和以上结论不一致,需要继续深入研究下。。。。。。。。。。。。。。。。。。。。未完待续 [fail-fast_fail-safe]: https://www.nowcoder.com/questionTerminal/95e4f9fa513c4ef5bd6344cc3819d3f7?pos=101&mutiTagIds=570&orderByHotValue=1
还没有评论,来说两句吧...