首页 > 代码库 > java.util.ConcurrentModificationException 异常解决办法及原理

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 = http://www.mamicode.com/map.get(s1);>

或者下面的方式同样也会抛出异常

		Iterator<String> iterator = map.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			String value = http://www.mamicode.com/map.get(key);>其实不管是Map还是Set这样操作时均会抛出此异常!

解决办法为:如果不是Iterator迭代方式,则修改map迭代方式为Iterator()方式,采用iterator.remove();而不直接通过map.remove();

                Iterator<String> iterator = map.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			String value = http://www.mamicode.com/map.get(key);>
详细原因如下:

发现这个位置应该是不会报错的,查找前后文,发现最有可能报错的应该是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同样自增。也就不会抛出异常。


java.util.ConcurrentModificationException 异常解决办法及原理