java.util.ConcurrentModificationException
Where ConcurrentModificationException raised
Case 1: update value Entries during traverse Map in same Thread
java.util.ConcurrentModificationException will be raised in following java code:
Map<String, String> pendingCorrelations = new Hashtable<String, String>();
pendingCorrelations.put("a", "a1");
pendingCorrelations.put("b", "b1");
pendingCorrelations.put("c", "c1");
pendingCorrelations.put("d", "d1");
pendingCorrelations.put("e", "e1");
Set<String> keys = pendingCorrelations.keySet();
for (String key : keys) { // Here raise ConcurrentModificationException after a is removed.
if (key.equalsIgnoreCase("a")) {
pendingCorrelations.remove(key);
}
}
Exception:
java.util.ConcurrentModificationException
at java.util.Hashtable$Enumerator.next(Hashtable.java:1031)
Case 2: This exception could happen in case many simultaneouse threads manipulate same value Object.
Why ConcurrentModificationException raised?
Here we focous on two points:
- How Java analyze for() element?
- What happened after Hashtable.remove(Object)?
We can image JDK will analyze for() as following:
for(String key=Hashtable$Enumerator.next();Hashtable$Enumerator.hasMoreElements();){
pendingCorrelations.remove(key);
}
An interesting source code to understand well for() elements:
Map<String, String> pendingCorrelationsMap = new HashMap<String, String>();
pendingCorrelationsMap.put("c", "c1");
pendingCorrelationsMap.put("b", "b1");
pendingCorrelationsMap.put("d", "b1");
pendingCorrelationsMap.put("a", "a1");
for (String key : pendingCorrelationsMap.keySet()) {
if (key.equalsIgnoreCase("a")) { // won't cause ConcurrentModificationException
pendingCorrelationsMap.remove(key);
}
}
for (String key : pendingCorrelationsMap.keySet()) {
if (key.equalsIgnoreCase("c")) { // will cause ConcurrentModificationException
pendingCorrelationsMap.remove(key);
}
}
java.util.Hashtable
/**
* The number of times this Hashtable has been structurally modified
* Structural modifications are those that change the number of entries in
* the Hashtable or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the Hashtable fail-fast. (See ConcurrentModificationException).
*/
private transient int modCount = 0;
/**
* The modCount value that the iterator believes that the backing
* Hashtable should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
protected int expectedModCount = modCount;
/**
* Removes the key (and its corresponding value) from this
* hashtable. This method does nothing if the key is not in the hashtable.
*
* @param key the key that needs to be removed
* @return the value to which the key had been mapped in this hashtable,
* or <code>null</code> if the key did not have a mapping
* @throws NullPointerException if the key is <code>null</code>
*/
public synchronized V remove(Object key) {
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
modCount++; // Only modCount updated within Hashtable.remove(Object)
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}
We can see that if any Object removed by invode Hashtable.remove(Object), only "modCount++" invoked (not expectedModCount).
java.util.Hashtable$Enumerator<T>
Hashtable$Enumberator<T>
/**
* A hashtable enumerator class. This class implements both the
* Enumeration and Iterator interfaces, but individual instances
* can be created with the Iterator methods disabled. This is necessary
* to avoid unintentionally increasing the capabilities granted a user
* by passing an Enumeration.
*/
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
...
public T next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}
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++; //both modCount and expectedModCount will be updated simultanously
expectedModCount++;
if (prev == null)
tab[index] = e.next;
else
prev.next = e.next;
count--;
lastReturned = null;
return;
}
}
throw new ConcurrentModificationException();
}
}
...
}
Hashtable$Enumberator.next() will check (modCount != expectedModCount) to fast fail. Here can see how "java.util.ConcurrentModificationException" raised.
Solutions
Solution 1: Update(Remove) java collections through Iterator
As we list in prior java source code, we can see both "modCount" and "expectedCount" will be udpated simultanousely in Hashtable$Enumberator which will be return when calling Hashtable.interator().
So we can remove elements as following without ConcurrentModificationException:
Iterator iter = Collections.iterator();
while(iter.hasNext()) {
iter.next(); // Iterator.next() must be called before calling remove()
iter.remove(); // should be called once and only once
}
Note:
- Here "Collections" is only a symbol of set of objects it is not "java.util.Collection".
- We can only invoke remove through Iterator. (because there is no other udpate operation API.)
Solution 2: Remove Objects from Collections outof Traverse Collections.
Map<String, String> pendingCorrelations = new Hashtable<String, String>();
pendingCorrelations.put("a", "a1");
pendingCorrelations.put("b", "b1");
pendingCorrelations.put("c", "c1");
pendingCorrelations.put("d", "d1");
pendingCorrelations.put("e", "e1");
Set<String> keys = pendingCorrelations.keySet();
List<String> tobeRemvoed = new ArrayList<String>();
for (String key : keys) {
if (key.equalsIgnoreCase("a")) {
tobeRemvoed.add(key);
// pendingCorrelations.remove(key);
}
}
assertEquals(5, pendingCorrelations.size());
for (String str : tobeRemvoed) {
pendingCorrelations.remove(str);
}
assertEquals(4, pendingCorrelations.size());
上一篇: 将博客搬至CSDN
下一篇: 清明节 一秒钟设置Html灰色背景
推荐阅读
-
java.util.ConcurrentModificationException
-
java.util.ConcurrentModificationException
-
出现java.util.ConcurrentModificationException 问题及解决办法
-
出现java.util.ConcurrentModificationException 问题及解决办法
-
Java集合:arraylist及java.util.ConcurrentModificationException
-
关于java List的remove方法导致的异常java.util.ConcurrentModificationException
-
java.util.ConcurrentModificationException详解
-
java.util.ConcurrentModificationException
-
Caused by: java.util.ConcurrentModificationException
-
java.util.ConcurrentModificationException