1.fail-fast和fail-safe比较

Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。

快速失败示例

import java.util.*;
import java.util.concurrent.*;/** @desc java集合中Fast-Fail的测试程序。**   fast-fail事件产生的条件:当多个线程对Collection进行操作时,若其中某一个线程通过iterator去遍历集合时,该集合的内容被其他线程所改变;则会抛出ConcurrentModificationException异常。*   fast-fail解决办法:通过util.concurrent集合包下的相应类去处理,则不会产生fast-fail事件。**   本例中,分别测试ArrayList和CopyOnWriteArrayList这两种情况。ArrayList会产生fast-fail事件,而CopyOnWriteArrayList不会产生fast-fail事件。*   (01) 使用ArrayList时,会产生fast-fail事件,抛出ConcurrentModificationException异常;定义如下:*            private static List<String> list = new ArrayList<String>();*   (02) 使用时CopyOnWriteArrayList,不会产生fast-fail事件;定义如下:*            private static List<String> list = new CopyOnWriteArrayList<String>();** @author skywang*/
public class FastFailTest {private static List<String> list = new ArrayList<String>();//private static List<String> list = new CopyOnWriteArrayList<String>();public static void main(String[] args) {// 同时启动两个线程对list进行操作!new ThreadOne().start();new ThreadTwo().start();}private static void printAll() {System.out.println("");String value = null;Iterator iter = list.iterator();while(iter.hasNext()) {value = (String)iter.next();System.out.print(value+", ");}}/*** 向list中依次添加0,1,2,3,4,5,每添加一个数之后,就通过printAll()遍历整个list*/private static class ThreadOne extends Thread {public void run() {int i = 0;while (i<6) {list.add(String.valueOf(i));printAll();i++;}}}/*** 向list中依次添加10,11,12,13,14,15,每添加一个数之后,就通过printAll()遍历整个list*/private static class ThreadTwo extends Thread {public void run() {int i = 10;while (i<16) {list.add(String.valueOf(i));printAll();i++;}}}}

2.如何解决

fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生**。**若在多线程环境下使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”。
所以,本例中只需要将ArrayList替换成java.util.concurrent包下对应的类即可。
即,将代码

private static List<String> list = new ArrayList<String>();

替换为

private static List<String> list = new CopyOnWriteArrayList<String>();

则可以解决该办法。

3. fail-fast原理

产生fail-fast事件,是通过抛出ConcurrentModificationException异常来触发的。
那么,ArrayList是如何抛出ConcurrentModificationException异常的呢?

我们知道,ConcurrentModificationException是在操作Iterator时抛出的异常。我们先看看Iterator的源码。ArrayList的Iterator是在父类AbstractList.java中实现的。代码如下:

package java.util;public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {...// AbstractList中唯一的属性// 用来记录List修改的次数:每修改一次(添加/删除等操作),将modCount+1protected transient int modCount = 0;// 返回List对应迭代器。实际上,是返回Itr对象。public Iterator<E> iterator() {return new Itr();}// Itr是Iterator(迭代器)的实现类private class Itr implements Iterator<E> {int cursor = 0;int lastRet = -1;// 修改数的记录值。// 每次新建Itr()对象时,都会保存新建该对象时对应的modCount;// 以后每次遍历List中的元素的时候,都会比较expectedModCount和modCount是否相等;// 若不相等,则抛出ConcurrentModificationException异常,产生fail-fast事件。int expectedModCount = modCount;public boolean hasNext() {return cursor != size();}public E next() {// 获取下一个元素之前,都会判断“新建Itr对象时保存的modCount”和“当前的modCount”是否相等;// 若不相等,则抛出ConcurrentModificationException异常,产生fail-fast事件。checkForComodification();try {E next = get(cursor);lastRet = cursor++;return next;} catch (IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}}public void remove() {if (lastRet == -1)throw new IllegalStateException();checkForComodification();try {AbstractList.this.remove(lastRet);if (lastRet < cursor)cursor--;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException e) {throw new ConcurrentModificationException();}}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}...
}

从中,我们可以发现在调用 next() 和 remove()时,都会执行 checkForComodification()。若 “modCount 不等于 expectedModCount”,则抛出ConcurrentModificationException异常,产生fail-fast事件。

要搞明白 fail-fast机制,我们就要需要理解什么时候“modCount 不等于 expectedModCount”!
从Itr类中,我们知道 expectedModCount 在创建Itr对象时,被赋值为 modCount。通过Itr,我们知道:expectedModCount不可能被修改为不等于 modCount。所以,需要考证的就是modCount何时会被修改。

接下来,我们查看ArrayList的源码,来看看modCount是如何被修改的。

package java.util;public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{...// list中容量变化时,对应的同步函数public void ensureCapacity(int minCapacity) {modCount++;int oldCapacity = elementData.length;if (minCapacity > oldCapacity) {Object oldData[] = elementData;int newCapacity = (oldCapacity * 3)/2 + 1;if (newCapacity < minCapacity)newCapacity = minCapacity;// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}}// 添加元素到队列最后public boolean add(E e) {// 修改modCountensureCapacity(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}// 添加元素到指定的位置public void add(int index, E element) {if (index > size || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);// 修改modCountensureCapacity(size+1);  // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1,size - index);elementData[index] = element;size++;}// 添加集合public boolean addAll(Collection<? extends E> c) {Object[] a = c.toArray();int numNew = a.length;// 修改modCountensureCapacity(size + numNew);  // Increments modCountSystem.arraycopy(a, 0, elementData, size, numNew);size += numNew;return numNew != 0;}// 删除指定位置的元素 public E remove(int index) {RangeCheck(index);// 修改modCountmodCount++;E oldValue = (E) elementData[index];int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index, numMoved);elementData[--size] = null; // Let gc do its workreturn oldValue;}// 快速删除指定位置的元素 private void fastRemove(int index) {// 修改modCountmodCount++;int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // Let gc do its work}// 清空集合public void clear() {// 修改modCountmodCount++;// Let gc do its workfor (int i = 0; i < size; i++)elementData[i] = null;size = 0;}...
}

从中,我们发现:无论是add()、remove(),还是clear(),只要涉及到修改集合中的元素个数时,都会改变modCount的值。

接下来,我们再系统的梳理一下fail-fast是怎么产生的。步骤如下:
(01) 新建了一个ArrayList,名称为arrayList。
(02) 向arrayList中添加内容。
(03) 新建一个“线程a”,并在“线程a”中通过Iterator反复的读取arrayList的值
(04) 新建一个“线程b”,在“线程b”中删除arrayList中的一个“节点A”。
(05) 这时,就会产生有趣的事件了。

在某一时刻,“线程a”创建了arrayList的Iterator。此时“节点A”仍然存在于arrayList中,创建arrayList时,expectedModCount = modCount(假设它们此时的值为N)。

在“线程a”在遍历arrayList过程中的某一时刻,“线程b”执行了,并且“线程b”删除了arrayList中的“节点A”。“线程b”执行remove()进行删除操作时,在remove()中执行了“modCount++”,此时modCount变成了N+1

“线程a”接着遍历,当它执行到next()函数时,调用checkForComodification()比较“expectedModCount”和“modCount”的大小;而“expectedModCount=N”,“modCount=N+1”,这样,便抛出ConcurrentModificationException异常,产生fail-fast事件。

至此,我们就完全了解了fail-fast是如何产生的!

即,当多个线程对同一个集合进行操作的时候,某线程访问集合的过程中,该集合的内容被其他线程所改变(即其它线程通过add、remove、clear等方法,改变了modCount的值);这时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。

4 解决fail-fast的原理

上面,说明了“解决fail-fast机制的办法”,也知道了“fail-fast产生的根本原因”。接下来,我们再进一步谈谈java.util.concurrent包中是如何解决fail-fast事件的。
还是以和ArrayList对应的CopyOnWriteArrayList进行说明。我们先看看CopyOnWriteArrayList的源码:

package java.util.concurrent;
import java.util.*;
import java.util.concurrent.locks.*;
import sun.misc.Unsafe;public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {...// 返回集合对应的迭代器public Iterator<E> iterator() {return new COWIterator<E>(getArray(), 0);}...private static class COWIterator<E> implements ListIterator<E> {private final Object[] snapshot;private int cursor;private COWIterator(Object[] elements, int initialCursor) {cursor = initialCursor;// 新建COWIterator时,将集合中的元素保存到一个新的拷贝数组中。// 这样,当原始集合的数据改变,拷贝数据中的值也不会变化。snapshot = elements;}public boolean hasNext() {return cursor < snapshot.length;}public boolean hasPrevious() {return cursor > 0;}public E next() {if (! hasNext())throw new NoSuchElementException();return (E) snapshot[cursor++];}public E previous() {if (! hasPrevious())throw new NoSuchElementException();return (E) snapshot[--cursor];}public int nextIndex() {return cursor;}public int previousIndex() {return cursor-1;}public void remove() {throw new UnsupportedOperationException();}public void set(E e) {throw new UnsupportedOperationException();}public void add(E e) {throw new UnsupportedOperationException();}}...}

从中,我们可以看出:

ArrayList和CopyOnWriteArrayList的区别

  1. 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。
  2. ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。
  3. ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!

快速失败(fail-fast)和安全失败(fail-safe)的区别相关推荐

  1. Java - Java集合中的快速失败Fail Fast 机制

    文章目录 什么是 fail-fast 源码解读 Itr 为什么对集合的结构进行修改会发生并发修改异常-源码分析 修改方法之 remove 修改方法之 add 案例分享 [案例一] [案例二] [案例三 ...

  2. 解决支付宝验证失败sign check fail: check Sign and Data Fail

    成功解决支付宝验证失败sign check fail: check Sign and Data Fail 解决方法: 在使用支付宝的沙漏环境开发测试支付功能的时候,报上面错误. 问题出现在一个参数上: ...

  3. 什么是Fail Fast和Fail Safe?

    这里是目录标题 1. Fail Fast a. 概念 b. 原理 c. 关注点 d. 注意 2. Fail Safe a. 概念 b. 原理 3. Fail Fast 和 Fail Safe的区别 1 ...

  4. 聊聊hikari与tomcat jdbc pool的fail fast

    序 本文主要研究在中途数据库挂的情况下,hikari与tomcat jdbc pool的fail fast情况. 实验代码 @Testpublic void testDatabaseDownAndUp ...

  5. 【转载】ArrayList 中数据删除 fail fast

    2019独角兽企业重金招聘Python工程师标准>>> 本文转载自http://shift-alt-ctrl.iteye.com/blog/1839147 在循环arrayLlist ...

  6. 【springcloud问题】Could not locate PropertySource and the fail fast property is set, failing

    问题描述:使用springcloud的本地配置中心时出现:Could not locate PropertySource and the fail fast property is set, fail ...

  7. Fail Fast与Fail Safe的区别

    Fail Fast Fail Fast Iterator在遍历集合时,若该集合发生了结构性的改变,则将抛出 ConcurrentModification 异常.例如: Map<String, S ...

  8. linux成功和失败的英文,成功与失败英文作文

    学会失败.成功没有捷径,历史上有成就的人,总是敢于行动,也会经常失败,不要让对失败的恐惧,绊住你尝试新事物的脚步.以下是学习啦小编分享给大家的关于成功与失败英文作文,欢迎大家前来参考! 成功与失败英文 ...

  9. ios 开发 微信分享失败_一个失落的孩子如何在失败的情况下从失败变成了iOS开发人员...

    ios 开发 微信分享失败 by Jordan LaGrone 乔丹·拉格隆(Jordan LaGrone) 一个失落的孩子如何在失败的情况下从失败变成了iOS开发人员 (How a lost kid ...

  10. sign check fail: check Sign and Data Fail

    支付宝开发报错:com.alipay.api.AlipayApiException: sign check fail: check Sign and Data Fail 解决方法: 确认使用的支付宝公 ...

最新文章

  1. c++ 对‘cv::waitKey(int)’未定义的引用
  2. STM32F030控制蜂鸣器
  3. 在云服务器上持续运行springboot项目
  4. spark.kubernetes.file.upload.path的作用
  5. HarmonyOS之深入解析Ability的功能和使用
  6. Martix工作室考核题 —— 输入一串数字,按要求打印。
  7. art-template用户注册方法
  8. 除了“团队牛”还有“饭菜香”,百度 IDL 招聘算法实习生
  9. 拓端tecdat|matlab从ECG信号数据趋势项的消除
  10. Python中从零开始的简单遗传算法
  11. [线筛五连]线筛欧拉函数
  12. 地平线谭洪贺:AI芯片怎么降功耗?从ISSCC2017说起
  13. typedef struct LNode *p和typedef struct LNode笔记
  14. 京东智联云贪心科技:图卷积神经网络在推荐系统的应用
  15. 怎么设置织梦栏目html结尾,dedecms网站栏目地址url优化成.html结尾的而不是文件夹形式结尾的。请大家来帮忙。...
  16. 开源免费 低代码平台开源_行动透明:免费代码营现已开源
  17. AUTOCAD——调整十字光标、CAD表格文字对齐方式
  18. FPGA(四):FPGA通过查表的方式生成正弦波
  19. android studio命令行差错,Android Studio构建正常,Gradle命令行失败
  20. android性能优化透明,Android性能优化-图片篇

热门文章

  1. 节约服务器成本50%以上,独角兽完美日记电商系统容器化改造历程
  2. 刚从 Nova 生出来的 Placement 是什么东西?
  3. 【精品分享】Kolla 让 OpenStack 部署更贴心
  4. Java画韦恩图_R绘制韦恩图 | Venn图
  5. 【优化算法】世界杯优化算法(WCOA)【含Matlab源码 1427期】
  6. 【辛烷值预测】基于matlab RBF神经网络辛烷值预测【含Matlab源码 177期】
  7. 【图像增强】基于matlab区域相似变换函数和蜻蜓算法灰度图像增强【含Matlab源码 089期】
  8. 【基础教程】基于matlab图像增强+复原+分割【含Matlab源码 056期】
  9. matlab利用窗改变幅值_Matlab在车辆中的应用 信号处理分析
  10. 例3.2 简单计算器 - 九度教程第27题(栈的应用)