集合类与Iterator的坑引发
2018-12-09 本文已影响0人
半数的年
http://naotu.baidu.com/file/43a7a6effe26277d6f837295e5413afd 学习脑图
https://www.bilibili.com/video/av35434533?from=search&seid=11201110952609046081 借鉴视频
题目:
/**
* 测试单线程下利用迭代器遍历ArrayList
*/
public static void testSingleList(){
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("小明");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String element = iterator.next();
if("李四".equals(element)){
list.remove(element);
}
System.out.println(element);
}
}
image.png
先解析下异常名字 ConcurrentModificationException:同步修改异常,字面上的理解就是在迭代器在迭代元素的时候,原来arraylist数组发生了变化,这样就导致了异常。
上面浅显的解析了一下,下面来看下源码深入理解一下。
image.pngArrayList的迭代器是一个Itr内部类,那就来看一下这个内部类
image.png内部类的三个成员变量
image.pngimage.png
这样我们就明白了为什么会list.remove()修改了modCount,就导致了同步修改异常
image.png那我们试一下将list.remove()改为iterator.remove(),发现就不报异常了,看下remove()源码
image.png我们看到在用迭代器删除元素会使期望修改次数 = 实际修改次数,这样在next()中判断的时候就相等了,也不会导致异常。
上面的是单线程的解决办法,而当使用多线程时,上面的使用迭代器的remove也不管用了。
/**
* 多线程下利用迭代器遍历ArrayList
*/
public static void testConcurrentArrayList(){
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("小明");
Thread thread1 = new Thread(()->{
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String element = iterator.next();
if("李四".equals(element)) {
iterator.remove();
}
}
}) ;
Thread thread2 = new Thread(()->{
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}) ;
thread1.start();
thread2.start();
}
image.png
因为在第一个线程在使用迭代器修改了list后,虽然迭代器的期望修改次数跟实际修改次数相等,而第二个线程在迭代的时候,创建的迭代器跟第一个线程迭代器是不一样的,在第一个线程修改后,第二个迭代器期望修改次数跟实际修改次数不一样了,这样在next()就会导致异常。
修改前: modCount == 线程0--expectModCount == 线程1--expectModCount
修改后: modCount + 1 == 线程0--expectModCount + 1 != 线程1--expectModCount
这样子的解决办法就是使用线程安全的CopyOnWriteArrayList
/**
* 多线程下利用迭代器遍历CopyOnWriteArrayList
*/
public static void testConcurrentCOWArrayList(){
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("小明");
Thread thread1 = new Thread(()->{
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String element = iterator.next();
if("李四".equals(element)) {
list.remove("李四");
// iterator.remove(); // CopyOnWriteArrayList中的迭代器不支持这种操作,调用这个方法会抛出异常
}
System.out.println("0---"+element);
}
}) ;
Thread thread2 = new Thread(()->{
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println("1---"+iterator.next());
}
}) ;
thread1.start();
thread2.start();
}