网上做任务网站,wordpress 多用户主页,有创意的网络公司名字大全,镇江教育云网站建设作者 | 七十一来源 | 程序员巴士前言什么是快速失败#xff1a;fail-fast 机制是java集合(Collection)中的一种错误机制。它只能被用来检测错误#xff0c;因为JDK并不保证fail-fast机制一定会发生。当多个线程对同一个集合的内容进行操作时#xff0c;就可能会产生fail-fas… 作者 | 七十一来源 | 程序员巴士前言什么是快速失败fail-fast 机制是java集合(Collection)中的一种错误机制。它只能被用来检测错误因为JDK并不保证fail-fast机制一定会发生。当多个线程对同一个集合的内容进行操作时就可能会产生fail-fast事件。运行如下代码即可出现异常// 关于fail-fast的一些思考
public class FailFastTest {public static void main(String[] args) {// 构建ArrayListListInteger list new ArrayList();list.add(1);list.add(2);list.add(3);list.add(4);for (int i : list) {list.remove(1);}}
}控制台会输出如下异常为什么要报这个错途中出错的地方是ArrayList中的代码定位到该处代码final void checkForComodification() {if (modCount ! expectedModCount)throw new ConcurrentModificationException();
}modCount是这个集合修改的次数这个属性来自AbstractList而我们的ArrayList是继承了该抽象类的。protected transient int modCount 0;expectedModCount又是啥呢当我们进行遍历时候debug一下发现进行forEach循环的时候其实走了下面这个方法iterator而且遍历这个底层还是走的hasNext方法public IteratorE iterator() {return new Itr();}判断是否有下一个元素public boolean hasNext() {return cursor ! size;}next()方法用于获取元素public E next() {checkForComodification(); // 留意这个方法int i cursor;if (i size)throw new NoSuchElementException();Object[] elementData ArrayList.this.elementData;if (i elementData.length)throw new ConcurrentModificationException();cursor i 1;return (E) elementData[lastRet i];}点进这个new Itr()惊喜的发现原来这个expectedModCount是在这里被赋值的而且和modCount一样private class Itr implements IteratorE {int cursor; // index of next element to returnint lastRet -1; // index of last element returned; -1 if no suchint expectedModCount modCount; // 注意此处进行赋值............接下来看下ArrayList的remove()方法其对modCount进行了增加这是导致报错的原因public E remove(int index) {rangeCheck(index);modCount; // 对modCount进行了的操作E oldValue elementData(index);int numMoved size - index - 1;if (numMoved 0)System.arraycopy(elementData, index1, elementData, index,numMoved);elementData[--size] null; // clear to let GC do its workreturn oldValue;}上面的next()方法这有调用一个checkForComodification()方法下面贴一下这方法的代码final void checkForComodification() {if (modCount ! expectedModCount)throw new ConcurrentModificationException();}ArrayList里面remove()方法进行了modCount操作原来是我们对集合进行操作后改变了modCount导致上面代码成立从而抛出异常但是当我们使用Itr类的remove也就是如下代码进行对元素改动时不会抛出ConcurrentModificationException异常public void remove() {if (lastRet 0)throw new IllegalStateException();checkForComodification();try {ArrayList.this.remove(lastRet);cursor lastRet;lastRet -1;// 将ArrayList的modCount赋值给Itr类的expectedModCount //这样再次调用next方法时就不会出现这俩个值不一致 从而避免报错expectedModCount modCount; } catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}与ArrayList的remove()方法不同的是该remove()方法调用ArrayList.this.remove(lastRet);后显然modCount了但是马上又让expectedModCount modCount就是这样才不会抛出异常。梳理整个流程1、for循环遍历实质上调用的是Itr类的方法进行遍历Itr类实现了Iterator2、Itr类在构造的时候会将ArrayList的modCount实际上modCount是AbstractList的属性但是ArrayList继承了AbstractList赋值给Itr类的expectedModCount3、for循环中调用的remove()方法时ArrayList的这个方法会对modCount进行操作4、remove方法调用后继续遍历会调用Itr的next()方法而这个next()方法中的checkForComodification()方法会对modCount和expectedModCount进行对比由于remove方法已经操作过modCount因此这俩个值不会相等故报错。如何改进1、可以使用Itr中的remove方法进行改进改进代码如下public static void main(String[] args) {// 构建ArrayListListInteger list new ArrayList();list.add(1);list.add(2);list.add(3);list.add(4);IteratorInteger iterator list.iterator();while(iterator.hasNext()) {iterator.next();iterator.remove();}System.out.println(list.size()); // 0}2、使用CopyOnWriterArrayList来代替Arraylist它对ArrayList的操作时会先复制一份数据出来操作完了再将其更新回去替换掉旧的所以CopyOnWrite容器只能保证数据的最终一致性不能保证数据的实时一致性。这是采用了CopyOnWriterArrayList的fail-safe机制当集合的结构被改变的时候fail-safe机制会在复制原集合的一份数据出来然后在复制的那份数据遍历fail-safe机制在JUC包的集合都是有这种机制实现的。虽然fail-safe不会抛出异常但存在以下缺点1、复制时需要额外的空间和时间上的开销。2、不能保证遍历的是最新内容。总结对于fail-fast机制我们要操作List集合时可以使用Iterator的remove()方法在遍历过程中删除元素或者使用fail-safe机制的CopyOnWriterArrayList当然使用的时候需要权衡下利弊结合相关业务场景。往期推荐低代码发展专访系列之八低代码平台能够打破企业「应用孤岛」现象吗Medusa又一个开源的替代品用了HTTPS没想到还是被监控了快速搭建实验环境使用 Terraform 部署 Proxmox 虚拟机点分享点收藏点点赞点在看