我的观点
fail-fast是什么就不多解释了,应该注意到的是(以ArrayList为例):modCount位于AbstractList中,

protected transient int modCount = 0;

并无volatile修饰,因此当两线程是共用同一个cpu时才会抛出并发修改异常。比如:

线程1正在用迭代器来读,此时共用同一个cpu**的线程2来修改list,使得modCount++。由于共用同一个cpu,那么所修改的是**同一个缓存中的modCount,这样使得线程1下一次检查时发现与期望值不等,便会抛出异常

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

但是如果线程2用的是不同的cpu,而modCount又没有volatile修饰,那么线程2对modCount的修改不知道什么时候才会写回主存,也不知道什么时候线程1才会重新从主存中读取modCount。
因此出现并发修改也不一定会抛异常,而其实只要违反规则,单线程照样会抛出并发修改异常

public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
Iterator iterator=list.iterator();
while(iterator.hasNext()){
iterator.next();
list.add(3);
}
}
// Exception in thread "main" java.util.ConcurrentModificationException
// at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
// at java.util.ArrayList$Itr.next(ArrayList.java:859)
// at github.com.AllenDuke.concurrentTest.future.FutureTest.main(FutureTest.java:29)

但是线程用哪个cpu执行任务是不可知的。

所见的网上的答案
注意:这里异常的抛出条件是检测到modCount != expectedModCount这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedModCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的变成,这个异常只建议用于检测并发修改的bug。

这句话会误让人以为,线程进去修改的时候+1,修改完就-1。但实际上modCount是只会递增的,至少在jdk1.8中没有发现modCount--或是--modCount。利用反射可以看出并不是退出方法就-1,如下:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ArrayList<Integer> list = new ArrayList<>();
Class c= AbstractList.class;
Field modCountField = c.getDeclaredField("modCount");
modCountField.setAccessible(true);
for (int i = 0; i < 5; i++) {
list.add(i);
System.out.println(modCountField.get(list));
}
}
// 1
// 2
// 3
// 4
//

或者这句话的意思是两个线程同时+1,这样的话,根本原因就和我的观点一致了。

解释为什么不能依赖fail-fast的更多相关文章

  1. 快速失败(fail—fast)和 安全失败(fail—safe)

    快速失败(fail-fast) 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的结构进行了修改(增加.删除),则会抛出Concurrent Modification Exception. 原理 ...

  2. Fail Fast and Fail Safe Iterators in Java

    https://www.geeksforgeeks.org/fail-fast-fail-safe-iterators-java/ Fail Fast and Fail Safe Iterators ...

  3. Spring IOC源代码具体解释之容器依赖注入

    Spring IOC源代码具体解释之容器依赖注入 上一篇博客中介绍了IOC容器的初始化.通过源代码分析大致了解了IOC容器初始化的一些知识.先简单回想下上篇的内容 加载bean定义文件的过程.这个过程 ...

  4. fail fast和fail safe策略

    优先考虑出现异常的场景,当程序出现异常的时候,直接抛出异常,随后程序终止 import java.util.ArrayList; import java.util.Collections; impor ...

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

    这是我遇到的问题 Could not locate PropertySource and the fail fast property is set, failing springcloud的其他服务 ...

  6. Java集合框架中的快速失败(fail—fast)机制

      fail-fast机制,即快速失败机制,是java集合框架中的一种错误检测机制.多线程下用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加.删除),则会抛出Concurre ...

  7. Java Gradle入门指南之依赖管理(添加依赖、仓库、版本冲突)

        开发任何软件,如何管理依赖是一道绕不过去的坎,软件开发过程中,我们往往会使用这样那样的第三方库,这个时候,一个好的依赖管理就显得尤为重要了.作为一个自动构建工作,Gradle对依赖管理有着很好 ...

  8. 简单解析依赖注入(控制反转)在Spring中的应用

    IoC——Inversion of Control  控制反转DI——Dependency Injection   依赖注入 大家都知道,依赖注入是Spring中非常重要的一种设计模式.可能很多初学者 ...

  9. AngularJS(15)-依赖注入

    AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或 ...

随机推荐

  1. TensorFlow——卷积神经网络的相关函数

    在TensorFlow中,使用tr.nn.conv2d来实现卷积操作,使用tf.nn.max_pool进行最大池化操作.通过闯传入不同的参数,来实现各种不同类型的卷积与池化操作. 卷积函数tf.nn. ...

  2. Apache Hudi使用问题汇总(一)

    1.如何写入Hudi数据集 通常,你会从源获取部分更新/插入,然后对Hudi数据集执行写入操作.如果从其他标准来源(如Kafka或tailf DFS)中提取数据,那么DeltaStreamer将会非常 ...

  3. cogs 293. [NOI 2000] 单词查找树 Trie树字典树

    293. [NOI 2000] 单词查找树 ★★☆   输入文件:trie.in   输出文件:trie.out   简单对比时间限制:1 s   内存限制:128 MB 在进行文法分析的时候,通常需 ...

  4. python 文件监听

    对文件进行监听.过滤 def tail(filename): f = open(file=filename, mode='r', encoding='utf-8') # 打开文件不能用with,因为监 ...

  5. 【python系统学习08】for循环知识点合集

    for循环 for简介 [循环]:就是依照某些我们编写的特定规则,重复的做一件事. 当你需要重复的"搬砖"的时候,可以用for循环进行遍历,让机器循环的帮你去"搬砖&qu ...

  6. python类型-字典

    字典是python语言中唯一的映射类型,映射对象里哈希值(键,key)和指向的对象(值,value)是一对多的关系.一个字典对象是可变的,是一个容器类型,可存储任意个数的python对象,其中也可以包 ...

  7. Kindle Unlimited 上线的最热书单

    Kindle 也给出了一份,到现在为止,在 Kindle Unlimited 上线的最热书单: 1.岛上书店2.一个人的朝圣3.自控力4.嫌疑人 X 的献身5.沉默的大多数(王小波文集)6.跟任何人都 ...

  8. NOI2.5 8465:马走日

    描述 马在中国象棋以日字形规则移动. 请编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点. 输入 第一行为整数T ...

  9. SpringCloud学习之搭建eureka集群,手把手教学,新手教程

    一.为什么需要集群 上一篇文章讲解了如何搭建单个节点的eureka,这篇讲解如何搭建eureka集群,这里的集群还是本地不同的端口执行三个eureka,因为条件不要允许,没有三台电脑,所以大家将就一下 ...

  10. Arrays.asList()用法梳理

    Arrays.asList()用法梳理 asList概述 Arrays是java容器相关操作的工具类,asList方法将Array转换为list,是Array和List之间的桥梁. asList本质 ...