for循环中list触发fast-fail或不触发的原理和方法

Iterable和Iterator

在这里插入图片描述
Iterator接口位于的位置是java.util.Iterator,它主要有两个抽象方法供子类实现。hasNext()用来判断还有没有数据可供访问,next()用来访问下一个数据。

集合Collection不是直接去实现Iterator接口,而是去实现Iterable接口。在这里插入图片描述
用这个Iterable接口的iterator()方法返回当前集合迭代器。
集合Collection体系的集合都得按照使用iterator()的方式返回可供遍历的迭代器iterator
在这里插入图片描述
集合使用Iterable接口的iterator()方法返回迭代器Iterator有很多好处。
在这里插入图片描述
比如有两个线程都需要使用迭代器进行遍历集合的操作,如果通过直接实现Iterator接口来拿迭代器。首先两者拿的是同一个迭代器,并且两者不同步,一个都遍历结束了,另一个都还没开始就无法遍历集合了,但是他本来循环从头开始遍历集合的所有数据的。

如果使用Iterable接口的iterator()方法返回迭代器Iterator,那两者获得的就是不同迭代器了,就互不影响,自己可以按照自己的进度遍历集合就行。
在这里插入图片描述


for-each就是迭代器的语法糖,增强版的 for 循环(也叫 “for-each” 循环)在 Java 中是一个特性,它允许你遍历任何实现了 Iterable 接口的集合或者数组。
要使用增强型 for 循环(也称为 for-each 循环)遍历一个集合,集合的类需要实现 java.lang.Iterable 接口。Iterable 接口定义了一个方法 iterator(),它返回一个 Iterator 对象,用于遍历集合的元素。

import java.util.Iterator;
import java.util.NoSuchElementException;

// Iterable位于java.lang包下,不用显式import
public class MyCollection<T> implements Iterable<T> {
    private T[] elements;
    private int size;

    @SuppressWarnings("unchecked")
    public MyCollection(int capacity) {
        elements = (T[]) new Object[capacity];
        size = 0;
    }

    public void add(T element) {
        if (size < elements.length) {
            elements[size++] = element;
        } else {
            throw new IllegalStateException("Collection is full");
        }
    }
	
	// 当一个类要实现 Iterable 接口,它必须提供一个 iterator() 方法,该方法返回一个 Iterator 对象。
    @Override
    public Iterator<T> iterator() {
        return new MyIterator();
    }

    private class MyIterator implements Iterator<T> {
        private int currentIndex = 0;

        @Override
        public boolean hasNext() {
            return currentIndex < size;
        }

        @Override
        public T next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return elements[currentIndex++];
        }
    }
}
MyCollection<String> collection = new MyCollection<>(10);
        collection.add("a");
        collection.add("b");
        collection.add("c");

        // 使用增强型 for 循环
        for (String element : collection) {
            System.out.println(element);
        }

        // 和增强for循环等价的显式迭代器循环
        // 当一个类实现了 Iterable 接口,它必须提供一个 iterator() 方法,该方法返回一个 Iterator 对象。
        // 这个 Iterator 对象实现了 hasNext() 和 next() 方法,用于遍历集合中的元素。
        // 下面的 iterator 是通过集合类实现的 Iterable 接口的 iterator() 方法获得的
        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);

快速失败机制的工作原理

fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件
Java中的集合类(如ArrayList、LinkedList、HashMap等)使用快速失败机制来检测在迭代过程中对集合的结构性修改。
工作原理如下:

  1. modCount:每个集合类都有一个modCount字段,用于记录集合结构性修改的次数。结构性修改是指添加或删除元素,改变集合的大小。
  2. 迭代器的expectedModCount:当创建迭代器时,迭代器会保存当前集合的modCount值到一个名为expectedModCount的字段中。
  3. 一致性检查:在每次调用迭代器的next()hasNext()方法时,迭代器首先会检查modCount是否与expectedModCount相同。如果不相同,说明集合在迭代过程中的结构被修改过,迭代器会抛出ConcurrentModificationException

总的来说:只要是涉及了改变ArrayList元素的个数的方法都会导致modCount的改变。当多线程环境下,由于expectedModCount与modCount的改变不同步,导致两者之间不等,从而产生fast-fail。

在这里插入图片描述
这里的抛出异常,停止执行就是fail-fast,就是当自己遇到无法处理的情况时的处理方式。

下面是ConcurrentModificationException异常的例子:

List<String> list = new ArrayList<>();
list.add("a");
Iterator<String> iterator = list.iterator();  // expectedModCount = modCount = 0
list.add("b");  // modCount = 1
iterator.next(); // expectedModCount != modCount, 抛出ConcurrentModificationException异常

for-each循环对集合进行增删也可能抛出异常,因为for-each在反编译下可以发现就是迭代器的语法糖,所以涉及到对迭代器的使用。

		List<String> collection = new ArrayList<>();
		collection.add("a");
		collection.add("b");
		// 使用增强型 for 循环
        for (String element : collection) {
            System.out.println(element);
        }

        // 和增强for循环等价的显式迭代器循环
        // 当一个类实现了 Iterable 接口,它必须提供一个 iterator() 方法,该方法返回一个 Iterator 对象。
        // 这个 Iterator 对象实现了 hasNext() 和 next() 方法,用于遍历集合中的元素。
        // 下面的 iterator 是通过集合类实现的 Iterable 接口的 iterator() 方法获得的
        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            if ("a".equals(element)) {
            	collection.remove(element);
            }
         }

想要在循环时进行增删操作,也就是进行这类对集合结构性有影响的操作,就要保证数据隔离性,下面是三种数据隔离性的处理方式,本质都是复制东西,只是复制的东西有所不同。
在这里插入图片描述
写入时复制(copy-on-write, 简称COW)。
在这里插入图片描述

在这里插入图片描述
GC 代表 “Garbage Collection”(垃圾回收)。COW读操作时并没有加锁,这是为了提高读操作的性能,但是有缺点,比如读数据的时候可能读不到最新的数据。例如,线程1往集合里面add数据才增加了一半,线程2这时候就去读数据,那读到的就还是老数据。

在这里插入图片描述
这样的话就只有增删才需要开辟一个新数组,其他情况都是使用原数组引用来读取原数组。

// 可以像使用普通的 ArrayList 一样使用 CopyOnWriteArrayList(写入时复制),并且可以通过 List 接口来引用它
        List<String> cowList = new CopyOnWriteArrayList<>();
        cowList.add("a");
        cowList.add("b");
        cowList.add("c");

        Iterator<String> iterator = cowList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        cowList.remove("b");
        Iterator<String> iterator2 = cowList.iterator();
        while (iterator2.hasNext()) {
            System.out.println(iterator2.next());
        }
  • 46
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重剑DS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值