1.为阅读体验,本站无任何广告,也无任何盈利方法,站长一直在用爱发电,现濒临倒闭,希望有能力的同学能帮忙分担服务器成本
2.捐助10元及以上同学,可添加站长微信lurenzhang888,备注捐助,网站倒闭后可联系站长领取本站pdf内容
3.若网站能存活下来,后续将会持续更新内容
快速失败
Java的快速失败机制是Java集合框架中的一种错误检测机制,当多个线程同时对集合中的内容进行修改时可能就会抛出ConcurrentModificationException
异常。其实不仅仅是在多线程状态下,在单线程中用增强for
循环中一边遍历集合一边修改集合的元素也会抛出ConcurrentModificationException
异常。看下面代码
public class Main{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for(Integer i : list){
list.remove(i); //运行时抛出ConcurrentModificationException异常
}
}
}
正确的做法是用迭代器的remove()
方法,便可正常运行。
public class Main{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
it.remove();
}
}
}
本站链接:https://www.mianshi.online,如需勘误或投稿,请联系微信:lurenzhang888
点击面试手册,获取本站面试手册PDF完整版
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
从上面代码中可以看到如果modCount
和expectedModCount
这两个变量不相等就会抛出ConcurrentModificationException
异常。那这两个变量又是什么呢?继续看源码
protected transient int modCount = 0; //在AbstractList中定义的变量
int expectedModCount = modCount;//在ArrayList中的内部类Itr中定义的变量
从上面代码可以看到,modCount
初始值为0,而expectedModCount
初始值等于modCount
。也就是说在遍历的时候直接调用集合的remove()
方法会导致modCount
不等于expectedModCount
进而抛出ConcurrentModificationException
异常,而使用迭代器的remove()
方法则不会出现这种问题。那么只能在看看remove()
方法的源码找找原因了
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
从上面代码中可以看到只有modCount++
了,而expectedModCount
没有操作,当每一次迭代时,迭代器会比较expectedModCount
和modCount
的值是否相等,所以在调用remove()
方法后,modCount
不等于expectedModCount
了,这时就了报ConcurrentModificationException
异常。但用迭代器中remove()
的方法为什么不抛异常呢?原来迭代器调用的remove()
方法和上面的remove()
方法不是同一个!迭代器调用的remove()
方法长这样:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; //这行代码保证了expectedModCount和modCount是相等的
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
从上面代码可以看到expectedModCount = modCount
,所以迭代器的remove()
方法保证了expectedModCount
和modCount
是相等的,进而保证了在增强for
循环中修改集合内容不会报ConcurrentModificationException
异常。
上面介绍的只是单线程的情况,用迭代器调用remove()
方法即可正常运行,但如果是多线程会怎么样呢?
答案是在多线程的情况下即使用了迭代器调用remove()
方法,还是会报ConcurrentModificationException
异常。这又是为什么呢?还是要从expectedModCount
和modCount
这两个变量入手分析,刚刚说了modCount
在AbstractList
类中定义,而expectedModCount
在ArrayList
内部类中定义,所以modCount
是个共享变量而expectedModCount
是属于线程各自的。简单说,线程1更新了modCount
和属于自己的expectedModCount
,而在线程2看来只有modCount
更新了,expectedModCount
并未更新,所以会抛出ConcurrentModificationException
异常。
安全失败
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会抛出ConcurrentModificationException
异常。缺点是迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生了修改,迭代器是无法访问到修改后的内容。java.util.concurrent
包下的容器都是安全失败,可以在多线程下并发使用。
本站链接:https://www.mianshi.online,如需勘误或投稿,请联系微信:lurenzhang888
点击面试手册,获取本站面试手册PDF完整版