Java的Hashtable在遍历时的迭代器线程问题(2)

[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类的部分源码如下:

复制代码

public <T> T[] toArray(T[] a) { synchronized(mutex) {return c.toArray(a);} } public Iterator<E> iterator() { return c.iterator(); // Must be manually synched by user! } public boolean add(E e) { synchronized(mutex) {return c.add(e);} } public boolean remove(Object o) { synchronized(mutex) {return c.remove(o);} }

代码中变量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型

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/f3e409710fddc43675099de40d87eca3.html