Android|Java 开发中的那些坑 ConcurrentModificationException
来源:互联网 发布:卢武铉在韩国名声知乎 编辑:程序博客网 时间:2024/05/17 08:19
开发中会遇到很多问题,比如:
public static void foreach(List<String> names) { for (String s : names) { names.remove(s); } }
会抛出 ConcurrentModificationException异常,但是下面这种写法:
public static void iterator(List<String> names) { Iterator<String> iterator = names.iterator(); while (iterator.hasNext()) { iterator.next(); iterator.remove(); } }
却不会抛出该异常!
我们看看这两个方法的字节码:
// access flags 0x9 // signature (Ljava/util/List<Ljava/lang/String;>;)V // declaration: void foreach(java.util.List<java.lang.String>) public static foreach(Ljava/util/List;)V L0 LINENUMBER 13 L0 ALOAD 0 INVOKEINTERFACE java/util/ Ljava/util/Iterator; ASTORE 1 L1 FRAME APPEND [java/util/Iterator] ALOAD 1 INVOKEINTERFACE java/util/Iterator.hasNext ()Z IFEQ L2 ALOAD 1 INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object; CHECKCAST java/lang/String ASTORE 2 L3 LINENUMBER 14 L3 ALOAD 0 ALOAD 2 INVOKEINTERFACE java/util/List.remove (Ljava/lang/Object;)Z POP L4 LINENUMBER 15 L4 GOTO L1 L2 LINENUMBER 16 L2 FRAME CHOP 1 RETURN L5 LOCALVARIABLE s Ljava/lang/String; L3 L4 2 LOCALVARIABLE names Ljava/util/List; L0 L5 0 // signature Ljava/util/List<Ljava/lang/String;>; // declaration: java.util.List<java.lang.String> MAXSTACK = 2 MAXLOCALS = 3
// access flags 0x9 // signature (Ljava/util/List<Ljava/lang/String;>;)V // declaration: void iterator(java.util.List<java.lang.String>) public static iterator(Ljava/util/List;)V L0 LINENUMBER 19 L0 ALOAD 0 INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator; ASTORE 1 L1 LINENUMBER 20 L1 FRAME APPEND [java/util/Iterator] ALOAD 1 INVOKEINTERFACE java/util/Iterator.hasNext ()Z IFEQ L2 L3 LINENUMBER 21 L3 ALOAD 1 INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object; POP L4 LINENUMBER 22 L4 ALOAD 1 INVOKEINTERFACE java/util/Iterator.remove ()V GOTO L1 L2 LINENUMBER 24 L2 FRAME SAME RETURN L5 LOCALVARIABLE names Ljava/util/List; L0 L5 0 // signature Ljava/util/List<Ljava/lang/String;>; // declaration: java.util.List<java.lang.String> LOCALVARIABLE iterator Ljava/util/Iterator; L1 L5 1 // signature Ljava/util/Iterator<Ljava/lang/String;>; // declaration: java.util.Iterator<java.lang.String> MAXSTACK = 1 MAXLOCALS = 2
我简化一下,把两个方法的字节码的关键部分贴出来:
foreach(List names):
Iterator.hasNext ()Z Iterator.next () List.remove (Ljava/lang/Object;)Z
iterator(List names):
Iterator.hasNext ()ZIterator.next ()Ljava/lang/Object;Iterator.remove ()V
我们先来看正确的iterator(List names)的调用:
Iterator.hasNext ()ZIterator.next ()Ljava/lang/Object;Iterator.remove ()V
看看Iterator.next()和Iterator.remove的源码:
public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
注意每次调用Iterator.next()和Iterator.remove()的时候都会调用:
checkForComodification()
而在该方法:
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
大家可以看到一旦出现 modCount != expectedModCount 就会抛出异常!
并且最关键的是每次调用Iterator.remove的时候,都会有:
expectedModCount = modCount;
这就保证了每次调用checkForCommodification()时不会导致
expectedModCount != modCount的情况出现
那我们来说说foreach(List names)为什么导致异常抛出
foreach(List names)的关键字节码为:
Iterator.hasNext ()Iterator.next ()List.remove (Ljava/lang/Object;)Z
跟iterator(List names):
Iterator.hasNext ()ZIterator.next ()Ljava/lang/Object;Iterator.remove ()V
不同的是 List.remove (Ljava/lang/Object;)Z
这个调用会直接调用:
private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
注意这里的modCount++,但是并没有对expectedModCount++,这就导致了:
expectedModCount != modCount的情况出现
而在下一次Iterator.next()方法中调用会执行:
在Iterator.hasNext()方法中有如下调用:
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
这时异常发生了!
当我们显示的使用Iterator的时候,每次调用都会保证:
modCount = expectedModCount
所以不会发生问题,但是当我们使用增强的foreach循环的时候,我们如果直接调用 List.remove,就会破坏Iterator的循环逻辑,导致 modCount != expectedModCount,问题的根源就在这里,所以当我们使用增强的foreach循环的时候,不用List.remove,同样也不要调用List.add操作!
- Android|Java 开发中的那些坑 ConcurrentModificationException
- java中的ConcurrentModificationException
- android java.util.ConcurrentModificationException
- Java 开发中的那些注意事项
- Android中的那些坑!
- Android开发那些坑
- java.util.ConcurrentModificationException 解决 Android
- javaweb开发中的那些坑
- Android 录音中的那些坑
- 项目中的java.util.ConcurrentModificationException异常
- Java表达式中的那些坑
- Android相机开发那些坑
- Android相机开发那些坑
- Android相机开发那些坑
- Android相机开发那些坑
- Android相机开发那些坑
- Android相机开发那些坑
- Android相机开发那些坑
- C++中重载函数模板
- 计算机丢失LIBEAY32.dll的解决方法
- [BZOJ2822][AHOI2012]树屋阶梯(卡特兰数+组合数学+高精度)
- 17-01-18
- HDU-1698-Just a Hook
- Android|Java 开发中的那些坑 ConcurrentModificationException
- Android Espresso UI测试
- 计算机中那些事儿(九):资料管理一些建议---理论篇
- Java考试题之十
- C++标准库函数swap的用法
- vue-cli的webpack模版,相关配置文件dev-server.js与webpack.config.js配置解析
- hive的功能架构
- 欢迎使用CSDN-markdown编辑器
- 静态、动态库的创建使用与makefile配置文件的编写