• java.util.ConcurrentModificationException 异常解决的方法及原理


    近期在修程序的bug,发现后台抛出下面异常:

    Exception in thread "main" java.util.ConcurrentModificationException
    	at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
    	at java.util.HashMap$KeyIterator.next(HashMap.java:828)
    	at com.keyman.demo.test.ClearResultTable.method2(ClearResultTable.java:54)
    	at com.keyman.demo.test.ClearResultTable.main(ClearResultTable.java:88)
    

    找到报错行:at com.keyman.demo.test.ClearResultTable.method2(ClearResultTable.java:54)发现,报错位置:for (String s1 : sets)

    		Set<String> sets = map.keySet();
    		for (String s1 : sets) {
    			String value = map.get(s1);
    			// 删除满足value以abc开头的键值对
    			if (value.startsWith("abc")) {
    				map.remove(s1);
    			}
    		}
    

    或者以下的方式相同也会抛出异常

    		Iterator<String> iterator = map.keySet().iterator();
    		while (iterator.hasNext()) {
    			String key = iterator.next();
    			String value = map.get(key);
    			// 删除满足value以abc开头的键值对
    			if (value.startsWith("abc")) {
    				map.remove(key); 
    				//iterator.remove(); // 同步modCount和expectedModCount
    				
    			}
    		}
    事实上无论是Map还是Set这样操作时均会抛出此异常!

    解决的方法为:假设不是Iterator迭代方式,则改动map迭代方式为Iterator()方式。採用iterator.remove();而不直接通过map.remove();

                    Iterator<String> iterator = map.keySet().iterator();
    		while (iterator.hasNext()) {
    			String key = iterator.next();
    			String value = map.get(key);
    			// 删除满足value以abc开头的键值对
    			if (value.startsWith("abc")) {
    				//map.remove(value); 
    				iterator.remove();   // 关键代码,同步modCount和expectedModCount
    				
    			}
    		}
    

    具体原因例如以下:

    发现这个位置应该是不会报错的。查找前后文,发现最有可能报错的应该是for循环里面,可是咋一看压根没错!通过查找资料发现:当改动的个数跟期望改动的个数不相等时抛出此异常。

            private abstract class HashIterator<E> implements Iterator<E> {
    	Entry<K, V> next; // next entry to return
    	int expectedModCount; // For fast-fail
    	int index; // current slot
    	Entry<K, V> current; // current entry
    	...
    	final Entry<K, V> nextEntry() {
    		if (modCount != expectedModCount)
    			throw new ConcurrentModificationException(); // 抛出异常
    		Entry<K, V> e = current = next;
    		if (e == null)
    			throw new NoSuchElementException();
    
    		if ((next = e.next) == null) {
    			Entry[] t = table;
    			while (index < t.length && (next = t[index++]) == null)
    				;
    		}
    		return e;
    	}
    	...
    }

    于是查看HashMap.remove()方法代码例如以下:

        /**
         * Removes the mapping for the specified key from this map if present.
         *
         * @param  key key whose mapping is to be removed from the map
         * @return the previous value associated with <tt>key</tt>, or
         *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
         *         (A <tt>null</tt> return can also indicate that the map
         *         previously associated <tt>null</tt> with <tt>key</tt>.)
         */
        public V remove(Object key) {
            Entry<K,V> e = removeEntryForKey(key);
            return (e == null ? null : e.value);
        }
    
        /**
         * Removes and returns the entry associated with the specified key
         * in the HashMap.  Returns null if the HashMap contains no mapping
         * for this key.
         */
        final Entry<K,V> removeEntryForKey(Object key) {
            int hash = (key == null) ? 0 : hash(key.hashCode());
            int i = indexFor(hash, table.length);
            Entry<K,V> prev = table[i];
            Entry<K,V> e = prev;
    
            while (e != null) {
                Entry<K,V> next = e.next;
                Object k;
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    modCount++;
                    size--;
                    if (prev == e)
                        table[i] = next;
                    else
                        prev.next = next;
                    e.recordRemoval(this);
                    return e;
                }
                prev = e;
                e = next;
            }
    
            return e;
        }
    你会发现,当中有modCount++操作。modCount表示改动的次数。而并没有改变其exceptedmodCount;

    接下来看看iterator.remove()方法:(java.util.Hashtable.Enumerator.remove())

    	public void remove() {
    	    if (!iterator)
    		throw new UnsupportedOperationException();
    	    if (lastReturned == null)
    		throw new IllegalStateException("Hashtable Enumerator");
    	    if (modCount != expectedModCount)
    		throw new ConcurrentModificationException();
    
    	    synchronized(Hashtable.this) {
    		Entry[] tab = Hashtable.this.table;
    		int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
    
    		for (Entry<K,V> e = tab[index], prev = null; e != null;
    		     prev = e, e = e.next) {
    		    if (e == lastReturned) {
    			modCount++;
    			expectedModCount++;
    			if (prev == null)
    			    tab[index] = e.next;
    			else
    			    prev.next = e.next;
    			count--;
    			lastReturned = null;
    			return;
    		    }
    		}
    		throw new ConcurrentModificationException();
    	    }
    	}
        }
    而此删除元素的方法,将modCount自增的同一时候将exceptedModCount相同自增。也就不会抛出异常。


  • 相关阅读:
    原码、反码、补码以及为什么要用反码和补码
    Linux中的段管理,bss段,data段,
    关于SRAM,DRAM,SDRAM,以及NORFLASH,NANDFLASH
    S3C2440的GPIO
    剑指offer——二叉搜索树与双向链表
    剑指offer——平衡二叉树
    ***剑指offer——字符串的排列(不会)
    剑指offer——两个链表的第一个公共结点
    剑指offer——数组中只出现一次的数字
    剑指offer——最小的K个数
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/6945602.html
Copyright © 2020-2023  润新知