在 Java并发编程实战(进阶篇) 中分析了 Vector 在迭代过程中对容器进行修改会抛出 ConcurrentModificationException 异常,但在并发容器中还会出现这种情况吗?
在并发容器中并不会出现这种情况,这是因为,util包中的迭代器实现是fast-failed迭代器(就是一旦由修改就抛异常),而在current包中迭代器是弱一致性迭代器。
那么我们通过 ConcurrentHashMap 源码分析一下这种弱一致性迭代器的工作原理:
测试代码在主线程向容器中预先添加一些元素,然后另外启动一个线程不断向容器中添加元素,同时在主线程使用迭代器遍历容器。此代码运行的结果是,不会抛出 ConcurrentModificationException ,同时添加到hashMap中的部分元素也会被迭代器迭代输出。
测试代码:
public static void main(String[] args) {
ConcurrentHashMap<String, String> hashMap =
new ConcurrentHashMap<>();
for (int i = 0; i < 100; i++) {
hashMap.put("" + i, "" + i);
}
Thread thread = new Thread(() -> {
for (int i = 200; i < 10000000; i++) {
hashMap.put("" + i, "" + i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
Iterator<String> iterator = hashMap.keySet().iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
System.out.println(key);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
出现这种情况也是我们希望并发容器出现的结果,那么查看源码分析:
调用 hashMap.keySet() ,返回了一个KeySetView内部类的实例
public KeySetView<K,V> keySet() {
KeySetView<K,V> ks;
return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));
}
keySet 是 ConcurrentHashMap 类中的私有变量,存储容器中的键的 Set 集合
KeySetView 静态内部类,重点分析 iterator 方法,返回 KeyIterator 迭代器实例
KeySetView 类图:
其实简单理解,KeySetView 相当于存放 ConcurrentHashMap 键的一个 Set 集合
public static