java.util包下的容器都是快速失败的,java.concurrent包下的容器都是安全失败的。
在使用迭代器遍历一个容器时。如果在遍历的过程中,这个容器被增删改了,那么这个遍历就会立刻终止并且抛出一个Concurrent Modification Exception异常。
class 容器{
int modCount;
...
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E>{
...
int expectedModCount = modCount;
}
}
看上面的代码,我们知道Iterator迭代器是容器类的一个内部类,我们创建迭代器的时候,迭代器会记录当前容器的修改的次数modCount。然后我们使用迭代器的hasNext()和next()方法去遍历的时候如果出现expectedModeCount和modCount不一致的情况,就会立马抛出异常,让上一层捕获,不再遍历。
这就是快速失败;“快速”指的是有修改则立马停止遍历,“失败”指的是遍历出错。
在迭代器遍历容器的时候,如果在遍历的过程中,集合被修改了,那么集合并不会抛出异常,不会崩溃而是会继续执行完遍历。“安全”在程序会接着运行,不会崩溃,“失败”在遍历的容器出错了,读不到新修改的元素,是数据一致性失败。
以CopyOnWriteArrayList为例,它的读写是不同的机制,读的时候不会有任何限制,所有线程都可以同时进行读。而写的时候会通过锁阻塞别的写操作,与其他地方不同的是,这里读和写不是互斥的,只有写和写是互斥的。写的时候会先获取锁,保证只有一个线程在写,然后再copy一份原来的容器,再在这个新的容器副本上写,写好之后再让原来容器的指针指向这个新的副本。所以在写的时候,读线程完全不受影响,它可以安全地读完容器元素,只是读的可能不是最新元素而已。
Fail-Fast 是指在遍历集合过程中如果检测到集合被结构性修改,立即抛出 ConcurrentModificationException
异常,保证数据一致性但不容忍并发修改;而 Fail-Safe 则采用读写分离或弱一致性策略,即使集合在遍历时被修改,也不会抛出异常,程序可以继续运行,但读取的数据可能不是最新的。前者注重一致性与快速错误发现,适用于单线程或低并发环境;后者注重容错性与线程安全,适用于读多写少的高并发场景。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。