ArrayList

ArrayList非线程安全,源码doc上同样说明Note that this implementation is not synchronized.在多线程环境下,启动线程A,while true不停循环静态变量list, 此时另一个线程B执行list.add操作,抛出ConcurrentModificationException.
如果使用增强for循环遍历list时删除指定元素(删除倒数第二个元素则不会异常,原因后续解释),需要使用遍历器Iterator删除,否则会抛出ConcurrentModificationException

# 增强for循环

# 语法糖

for循环本质是封装好的语法糖,隐藏了内部的实现细节,一段简单的for循环,经编辑器反编译后.class文件显示如下 img.png 流程解释 img.png 现在可以尝试解释,为什么并发情况会出现ConcurrentModificationException,当线程A调用list.remove方法会修改modCount++,线程B遍历Itr.expectedModCount不会随List.modCount 改变而改变,所以在next方法的校验中,就会抛出异常。得知原理后,那么单线程环境下,List.remove方法同样会改变modCount,所以List推荐使用Iterator方式删除。

# 特殊情况

那为什么删除集合中倒数第二个元素不会抛出ConcurrentModificationException呢?
  首先我们从上文中已知两个结论,for语法糖变成了while(Itr.hasNext()),其次是Exception其实在循环体中的Itr.next()方法中抛出。 抛出异常的根源的是modCount属性被修改,如果程序在删除元素后压根没有执行到循环体中,也就是没有执行next方法,合理猜测就不会抛出异常,进入源码可知list.remove()在删除元素后, 把List.size--,再回到Itr.hasNext方法判断逻辑是cursor!=size,假设list初始有5个元素,size=5,此时遍历到第四个删除后,size减1=4,cursor+1,指向下一个元素,也就是最后一个,所以 cursor=4,size=cursor,所以不满足while条件,所以有惊无险的没有执行next方法,也就没有报错。

# 思考

如果删除倒数第二个元素不报错,那么删除最后一个呢?为什么呢?
答案当然还有抛出CME,继续上个例子,原因在于遍历最后一个元素后,此时cursor指向下一个元素,所以cursor=5,而删除最后一个元素后,size=4,所以满足Itr.hasNext()条件,然后执行到next()方法内部, 第一关检查checkForComodification就直接露了原形,由于最后元素删除,导致modCount++,不等于初始状态的expectedModCount,所以抛异常啦

  • 单线程如何解决遍历中删除元素的异常?
方法一:
    使用Iterator的remove方法,因为此方法会重新赋值expectedModCount=modCount
方法二:
    使用CopyOnWriteArrayList,因为该list中没有expectedModCount变量,所以不校验是否相等。(多线程环境下同样可以使用方法二)
1
2
3
4