原文地址: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 }