个人观点
fail-fast是什么就很少解释了,应该注意到的是(以ArrayList为例):modCount位于AbstractList中,java
protected transient int modCount = 0;
并没有volatile修饰,所以当两线程是共用同一个cpu时才会抛出并发修改异常。好比:git
线程1正在用迭代器来读,此时共用同一个cpu**的线程2来修改list,使得modCount++。因为共用同一个cpu,那么所修改的是**同一个缓存中的modCount,这样使得线程1下一次检查时发现与指望值不等,便会抛出异常github
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。spa
这句话会误让人觉得,线程进去修改的时候+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 // 5
或者这句话的意思是两个线程同时+1,这样的话,根本缘由就和个人观点一致了。code