zoukankan      html  css  js  c++  java
  • 从 modCount 看 java集合 fail-fast 机制

    一、背景

    在常见的Java的非线程安全集合类中(如HashMap、ArrayList),经常可以在一些修改结构的操作(如Add)中看到实例变量 modCount++ ,来统计集合的修改次数。
    从注释也可以看出,该字段是为 fail-fast(快速失败)机制服务。

    二、简介

    fail-fast 机制是能立刻报告任何可能导致失败的错误检测机制。

    在java集合框架中表现为:当构建迭代器时,起初expectedModCount = modCount,当修改了该集合时,则该集合modCount++,随后迭代器在迭代下个元素时,会发现当前的modCount值与期望值expectedModCount不符,便会抛出ConcurrentModificationException异常,以避免数据不同步带来的麻烦。

    注:

    1. 使用迭代器的remove()方法不会抛出异常,看源码可知在其中重新设置expectedModCount。
    2. 该机制不保证存在非同步并发修改时一定会抛出异常,只是尽最大努力去抛出该异常,因此最好不要依赖于该异常去写程序,而只是用于debug阶段。

    官方注释:
    Note that fail-fast behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.

    三、两种出现场景

    3.1 单线程环境

    在遍历集合的过程中,调用了集合修改方法。
    例:

    class Test{
        public static void main(String[] args){
            ArrayList<Integer> list = new ArrayList();
            list.add(1); list.add(2); list.add(3);
    
            Iterator itr = list.iterator();
            while(itr.hasNext()){
                System.out.println(itr.next());// 1 
     2 
     3
                itr.remove();  //后续不会抛出异常
            }
            System.out.println(list.toString());// 输出:[]
            list.add(1); list.add(2); list.add(3);
    
            itr = list.iterator();
            while(itr.hasNext()){
                Object i = itr.next();
                System.out.println(i);
                list.remove(i);  //后续next时会抛出异常
            }
        }
    }
    

    3.2 多线程环境

    一个线程在遍历该集合时,另一个线程修改了该集合结构。

        ArrayList<Integer> list = new ArrayList();
        list.add(1); list.add(2); list.add(3);
    	
        for(Integer i: list){
            new Thread(() -> {
                list.add(4);
            }).run();
            System.out.println(i);
        }
    

    四、迭代器源码解析

    以ArrayList对Iterator的实现为例:

        private class Itr implements Iterator<E> {
            int cursor;       // index of next element to return
            int lastRet = -1; // index of last element returned; -1 if no such
            int expectedModCount = modCount;  // 期望的modCount值即为创建迭代器时的modCount值
    
            Itr() {}
    
            public boolean hasNext() { // hasNext 并不抛异常
                return cursor != size;
            }
    
            @SuppressWarnings("unchecked")
            public E next() {
                checkForComodification(); //首先检查expectedModCount是否一致
                // …省略
            }
            
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;  //迭代器remove后不抛异常的原因,更新 expectedModCount
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            /** 检查 expectedModCount 与 当前 modCount是否一致,否则抛异常*/
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    

    参考:
    https://www.geeksforgeeks.org/fail-fast-fail-safe-iterators-java/
    https://blog.csdn.net/u014692324/article/details/78577130

  • 相关阅读:
    【笔记】Eclipse and Java for Total Beginners—015
    [笔记]再笔记边干边学Verilog HDL –006
    [笔记]再笔记边干边学Verilog HDL 002
    【笔记】Eclipse and Java for Total Beginners—016
    这是第一篇博客文章
    c语言读取obj文件转换数据
    腾讯研发项目总监:互联网产品开发中的“快”字诀
    截取年月日错误笔记
    我在赶集网的两个月
    庆云书店叹息中关门
  • 原文地址:https://www.cnblogs.com/simpleito/p/10901667.html
Copyright © 2011-2022 走看看