小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
- 以前在看JDK集合源码时,会发现其中有快速失败和失败安全机制的应用实现(以前刚开始看时不知道这个东东是什么意思,后来了解多了才知道这个-_-)
- 快速失败和失败安全机制其实是一种设计思想,其应用场景不只在Java集合类,很多开源框架里也有提现,Dubbo的集群容错机制,其中除了有快速失败和失败安全机制外,还有失败自动切换、失败自动回复、并行调用多个服务机制。
- 本人对dubbo框架了解不多,本次只讨论JDK集合内快速失败和失败安全机制的实现。
快速失败机制
public static void main (String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
for (Integer num : list) {
if (2 == num) {
list.remove(num);
}
}
System.out.println(list);
}
复制代码
- 此代码执行结果是什么?
- 很明显这题其实不难,不会打印,直接抛异常:java.util.ConcurrentModificationException
- 根据异常信息,在ArrayList.java:909行抛异常,点进去看会发现:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
复制代码
- 当满足条件modCount != expectedModCount,即会抛出ConcurrentModificationException。
- 那就看看这两个参数是什么意思。 并且在哪种情况下会满足modCount != expectedModCount。
modCount
- 本人英语不好没法整段翻译,着重看图中圈起来的位置说明其fail-fast(快速失败),并且在add和remove方法内对其自增。
- 上图分别是add方法和remove方法里对modCount的自增操作
expectedModCount
- 在集合中的一个内部类Itr中,有一个属性expectedModCount,根据字面翻译:预期修改次数
int expectedModCount = modCount;
复制代码
- 在Itr对象里调用remove方法是,会再次进行赋值操作。
modCount != expectedModCount
- 首先去除Java语法糖机制,看当前类加载后的class文件
- 在当前foreach循环删除的场景里,foreach最后会被编译成使用Iterator对象。
- 这里直接调用集合的remove方法,那么会导致modCount,然而Iterator对象里的expectedModCount属性没有更新,所以在使用Iterator对象获取元素时的前置检测不通过,抛出异常:ConcurrentModificationException。
- 如果使用Iterator对象的remove方法进行删除元素,会对expectedModCount进行赋值操作,以确保modCount == expectedModCount。并发时需要对Iterator对象进行加锁
注:上诉前置检测抛异常原理其实就是快速失败机制的体现,在迭代器访问集合元素时,如果集合内的元素有改动操作,则立即抛出异常。
复制代码
失败安全机制
- 和快速失败不同,安全失败是调用过程出现了异常,则是不抛出只记录或打印,然后继续执行后续流程。
- 在java.util.concurrent的并发集合内都是使用安全失败机制,在遍历集合时不是在原集合上进行遍历,而是直接复制一份原集合内容,在复制的集合内容上进行遍历操作
- 使用复制原集合内容方式能避免快速失败机制,但是会增加内存占用,并且一致性无法保证,只能保证数据的最终一致性。
- java.util.concurrent下并发集合可在多线程中并发执行,因为是不会操作原集合内容,所以执行过程中互不影响,都会执行后续流程。
public static void main (String[] args) {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
list.add(1);
list.add(2);
final Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
if (2 == next) {
list.remove(next);
}
}
System.out.println(list);
}
复制代码
最后
- 如果上述表达有任何问题,欢迎指出!
- 最后虚心学习,共同进步 -_-
近期评论