原文地址:ConcurrentModificationException异常解决办法做者:心灵征途 java.util.ConcurrentModificationException异常(转) 一、今天在写一个带缓存功能的访问代理程序时出现了java.util.ConcurrentModificationException异常, 由于该异常是非捕获型异常并且不多见,因此费了些时间才找到问题所在,原来在经过Iterator进行遍历的时候,若是直接对HashMap进行操做后,再继续用以前的Iterator进行遍历就会出现这个异常,表示其HashMap已经被修改。 源程序代码片断以下:caches为一个HashMap对象 String sameKeyPart = accesserClassName + "@" + methodName + "@" + parameterKeyString + "@"; Iterator keys = caches.keySet().iterator(); String key = null; while (keys.hasNext()) ...{ key = (String) keys.next(); if (key.startsWith(sameKeyPart)) ...{ caches.remove(key); } } 解决办法为经过其相应的Iterator进行删除就能够了,修改后代码片断以下: String sameKeyPart = accesserClassName + "@" + methodName + "@" + parameterKeyString + "@"; Iterator keys = caches.keySet().iterator(); String key = null; while (keys.hasNext()) ...{ key = (String) keys.next(); if (key.startsWith(sameKeyPart)) ...{ keys.remove(); } }java
二、 撰写多线程代码时,你遇到过多少次下面的提示: Exception in thread "main" java.util.ConcurrentModificationException数据库
这个异常产生的缘由有几个。一是直接对集合调用删除操做而不是在枚举器上。二是不一样的线程试图对集合进行增删操做的时候。 这个解决办法的第一步就是同步代码,使得你在枚举的时候其它的线程不能增删记录。可是若是每一个枚举过程要进行复杂的计算或者是数据库访问的一部分的话,这个同步就会致使可怕的后果。为了减小负面影响,能够拷贝一个只读的枚举器,去掉同步,而后采用下列代码所示的方法: private List list; public void add(Object obj) { synchronized(list) { list.add(obj); } } public void perform( ) { Iterator iterator = null; synchronized(list) { iterator = new CopiedIterator(list.iterator( )); } while(iterator.hasNext( )) { // perform resource or cpu hungry work } } 重要的是记住,CopiedIterator不是一个克隆,只是一个只读的拷贝,因此它并无保持原有的所有功能。最重要的是,不能再调用CopiedIterator.remove方法了。CopiedIterator.remove的实现以下: public class CopiedIterator implements Iterator { private Iterator iterator = null; public CopiedIterator(Iterator itr) { LinkedList list = new LinkedList( ); while(itr.hasNext( )) { list.add(itr.next( )); } this.iterator = list.iterator( ); } public boolean hasNext( ) { return this.iterator.hasNext( ); } public void remove( ) { throw new UnsupportedOperationException("This is a read-only iterator."); } public Object next( ) { return this.iterator.next( ); } } 枚举器的只读拷贝将用在同步状态上的时间减小到最小,所以能够加强全局的效率。 三、 当使用 fail-fast iterator 对 Collection 或 Map 进行迭代操做过程当中尝试直接修改 Collection / Map 的内容时,即便是在单线程下运行, java.util.ConcurrentModificationException 异常也将被抛出。 Iterator 是工做在一个独立的线程中,而且拥有一个 mutex 锁。 Iterator 被建立以后会创建一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,因此当索引指针日后移动的时候就找不到要迭代的对象,因此按照 fail-fast 原则 Iterator 会立刻抛出 java.util.ConcurrentModificationException 异常。 因此 Iterator 在工做的时候是不容许被迭代的对象被改变的。但你可使用 Iterator 自己的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。 有意思的是若是你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为何在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.缓存
1 import java.util.*; 2 3 public final class MyTest 4 { 5 private static HashMap p_mapList = new HashMap(2); 6 private MyTest(){} 7 public static void init(){ 8 // If only there are more than one element in Map, 9 // the ConcurrentModificationException will not be 10 // thrown. 11 p_mapList.put(new String("hello"),new String("world")); 12 p_mapList.put(new String("goto"),new String("hell")); 13 } 14 public static void clear() throws Exception{ 15 Iterator pTmpKeys = null; 16 Long pTmpKeyLong; 17 pTmpKeys = p_mapList.keySet().iterator(); 18 String pCurKey = null; 19 String pCurObj = null; 20 while(pTmpKeys.hasNext()){ 21 pCurKey = (String) pTmpKeys.next(); 22 pCurObj = (String) p_mapList.get(pCurKey); 23 24 p_mapList.put(pCurKey,null); 25 // You can not remove element in Map object directly. 26 //p_mapList.remove(pCurKey); 27 // But you can remove current element by iterator itself. 28 pTmpKeys.remove(); 29 30 System.out.println(pCurKey + " removed."); 31 } 32 System.out.println(p_mapList.size() + " entries left after iterator."); 34 pTmpKeys = null; 35 } 36 public static void main(String[] args) throws Exception{ 38 MyTest.init(); 39 MyTest.clear(); 40 } 41 }