[Thread-1]Cache中新增缓存>>1418207644572
[Thread-1]Cache中新增缓存>>1418207645586
[Thread-1]Cache中新增缓存>>1418207646601
[Thread-1]Cache中新增缓存>>1418207647616
[Thread-1]Cache中新增缓存>>1418207648631
[Thread-1]Cache中新增缓存>>1418207649646
[Thread-1]Cache中新增缓存>>1418207650661
[Thread-1]Cache中新增缓存>>1418207651676
[Thread-1]Cache中新增缓存>>1418207652690
[Thread-1]Cache中新增缓存>>1418207653705
[Thread-0]删除的Key值<<1418207644572
Exception in thread "Thread-0" java.util.ConcurrentModificationException
at java.util.Hashtable$Enumerator.next(Unknown Source)
at org.cnblog.test.HashtableIteratorTest$Cache.refresh(HashtableIteratorTest.java:53)
at org.cnblog.test.HashtableIteratorTest$Cache.run(HashtableIteratorTest.java:64)
上述代码第53行,迭代缓存Map的时候抛出了java.util.ConcurrentModificationException异常。
解决过程首先,ConcurrentModificationException在JDK中的描述为:
当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
很奇怪,我明明在refresh()中对cacheMap遍历时,已经对cacheMap对象加锁,可是在next的时候仍然抛出了这个异常。
于是查看JDK源码,发现:
在cacheMap.keySet()时public Set<K> keySet() { if (keySet == null) keySet = Collections.synchronizedSet(new KeySet(), this); return keySet; }
KeySet是Set接口的一个子类,是Hashtable的内部类。返回的是将KeySet经过加锁后的包装类SynchronizedSet的对象。
SynchronizedSet类的部分源码如下:
代码中变量c为KeySet对象,mutex为调用keySet()方法的对象,即加锁的对象为cacheMap。(Collections同步Set的原理)
注意代码中iterator()方法中的注释:用户必须手动同步!
于是笔者仿佛找到了一些头绪。
在获取迭代器时,cacheMap.keySet().iterator():KeySet的iterator()方法最终返回的是Enumerator的对象,Enumerator是Hashtable的内部类。以下截取重要代码:
1 public T next() { 2 if (modCount != expectedModCount) 3 throw new ConcurrentModificationException(); 4 return nextElement(); 5 } 6 7 public void remove() { 8 if (!iterator) 9 throw new UnsupportedOperationException(); 10 if (lastReturned == null) 11 throw new IllegalStateException("Hashtable Enumerator"); 12 if (modCount != expectedModCount) 13 throw new ConcurrentModificationException(); 14 15 synchronized(Hashtable.this) { 16 Entry[] tab = Hashtable.this.table; 17 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; 18 19 for (Entry<K,V> e = tab[index], prev = null; e != null; 20 prev = e, e = e.next) { 21 if (e == lastReturned) { 22 modCount++; 23 expectedModCount++; 24 if (prev == null) 25 tab[index] = e.next; 26 else 27 prev.next = e.next; 28 count--; 29 lastReturned = null; 30 return; 31 } 32 } 33 throw new ConcurrentModificationException(); 34 } 35 }
可以看到,问题的发生源头找到了,当modCount != expectedModCount时,就会抛出异常。
那么,modCount和expectedModCount是做什么的?
modCount和expectedModCount是int型