博客
关于我
LinkedList(3):并发异常
阅读量:791 次
发布时间:2023-01-31

本文共 2848 字,大约阅读时间需要 9 分钟。

在分析以上两种用例时,可以发现主要问题集中在间接结构的modCount变量维护以及迭代器中modCount检查实现的细节。以下是详细的分析过程:

在第一个例子中,代码段如下:

public class TestLinkedList {    public static void main(String[] args) {        LinkedList linkedList = new LinkedList();        linkedList.add(11);        linkedList.add(22);        linkedList.add(33);                Iterator iterator = linkedList.iterator();        while (iterator.hasNext()) {            linkedList.add(10);  // 数据结构发生改变            System.out.println(iterator.next());        }        System.out.println(linkedList);    }}

在这个过程中,迭代器iterator被获取后,每次循环先执行linkedList.add(10),这会修改数据结构,导致modCount增加。在接下来的iterator.next()中,迭代器发现内部的modCount与自己保存的expectedModCount不一致,从而抛出ConcurrentModificationException

在第二个例子中,代码段如下:

public class TestLinkedList {    public static void main(String[] args) {        LinkedList linkedList = new LinkedList();        linkedList.add(11);        linkedList.add(22);        linkedList.add(33);                ListIterator iterator = linkedList.listIterator();        while (iterator.hasNext()) {            iterator.add(10);  // 数据结构发生改变            System.out.println(iterator.next());        }        System.out.println(linkedList);    }}

在这个递增的过程中,虽然数据结构被修改,但迭代器没有抛出异常。这可能是因为ListIterator的实现方式在处理modCount时具有不同机制,或者在迭代过程中导致modCount的增加与检查机制不同。

细节分析:

  • modCount的使用

    • Java集合类如LinkedList维护一个modCount变量,记录对集合进行操作次数。
    • 每个迭代器在创建时记录当前的modCount值(保存在expectedModCount变量中)。
    • 当迭代器的hasNextnext方法被调用时,内部会检查自身的modCount是否等于当前LinkedListmodCount。如果不等,抛出ConcurrentModificationException
  • 迭代器的生命周期

    • 迭代器一旦获取到集合的数据,开始迭代。此时,expectedModCount被设为当前集合的modCount
    • 在迭代过程中,每当原始集合(如linkedList)被修改时,modCount会被增加。
    • 当迭代器发现自身记录的expectedModCount不再等于当前集合的modCount时,表示数据结构被修改,抛出异常。
  • 默认实现与自定义实现的差异

    • 测试用例中,由于使用IteratorListIterator,两者的内部分析和modCount检查方式不同。
    • ListIterator通常提供的是弱一致性迭代器,而Iterator则要求强一致性。这可能导致在同样的数据操作下,是否会抛出异常的不同。
  • 举例如中的问题“为什么会抛出异常”

    • 在第一个例子中,迭代器在获取下一个元素之前,数据结构被修改,导致modCount被改变。
    • 迭代器在检测到modCount变化时,立即抛出并发异常,阻止潜在的数据不一致问题。
    • 这种设计意在防止数据结构被改写(例如增删改查),使得迭代操作是安全且一致的。
  • 测试用例中的修改行为

    • 在第一个例子中,循环内部先进行了add(10),导致数据结构变化,从而modCount增加。
    • 迭代器在接下来的next()操作中进行modCount检查,发现在获取时modCount已经发生了变化,符合预期,所以抛出异常。
    • 第二个例子中的ListIterator在进行add(10)时是否会抛出异常,具体取决于ListIterator的实现。一般来说,ListIterator的默认实现是支持检测到数据结构修改并抛出异常的。
  • 思想与表现的区别

    • 强一致性迭代器(如Iterator)在结构修改发生时就抛异常。
    • 弱一致性迭代器(如ListIterator)则允许在某些修改操作下继续使用,直到下一次操作时才可能抛出异常。
  • 实际应用中的做法

    • 通常,在需要对集合进行迭代操作时,使用默认的强一致性迭代器,以确保在迭代过程中的结构安全。
    • 只有在知道迭代操作不会被其他线程修改数据的情况下,才可以使用弱一致性迭代器,以提高性能。
    • 为了确保性能,ListIterator允许插入操作,但这会影响modCount的变化检测,导致更多封闭条件。
  • 优化建议:

  • 理解迭代器的类型

    • 确定需要使用强一致性还是弱一致性迭代器。
    • 强一致性迭代器在检测到结构修改时严格抛出异常。
    • 弱一致性迭代器更适合不频繁修改数据结构的情况。
  • 检查集合的modCount

    • 在迭代器中,每次调用hasNextnext时,总是比较当前modCount并抛出异常当有变化。
  • 遵守封闭封装原则

    • 在结构安全的情况下,确保modCount的改写不会导致并发异常。
  • 方式探究

    • 如何通过不同的迭代器类型和数据结构来测试并发修改的效果。
    • 理解在不同情况下,强一致性、弱一致性迭代器哪种更适合。
  • 总结:

    在上述代码中,第一个例子使用迭代器在遍历过程中对数据结构进行了修改,导致modCount发生变化,进而抛出并发异常。这是通过强一致性迭代器设计的一种防御机制。而第二个例子使用弱一致性迭代器,允许在添加操作后继续使用,并在后续检测到数据结构变化时才抛出异常。这表明在实际应用中,选择合适的迭代器类型对性能和正确性至关重要。

    转载地址:http://ckwfk.baihongyu.com/

    你可能感兴趣的文章
    leetcode题解131-分割回文串
    查看>>
    leetcode题解136-只出现一次的数字
    查看>>
    leetcode题解14-最长公共前缀
    查看>>
    leetcode题解151-翻转字符串里的单词
    查看>>
    leetcode题解153-寻找旋转排序数组的最小值
    查看>>
    leetcode题解167-两数之和 II - 输入有序数组
    查看>>
    leetcode题解172-阶乘后的零
    查看>>
    leetcode题解173-二叉搜索树迭代器
    查看>>
    leetcode题解189-旋转数组
    查看>>
    leetcode题解191-位1的个数
    查看>>
    leetcode题解20-有效的括号
    查看>>
    leetcode题解200-岛屿数量
    查看>>
    leetcode题解206-反转链表
    查看>>
    leetcode题解227-基本计算器 II
    查看>>
    leetcode题解236-二叉树的最近公共祖先
    查看>>
    leetcode题解25-K个一组翻转链表
    查看>>
    leetcode题解279-完全平方数
    查看>>
    leetcode题解3-无重复字符的最长子串
    查看>>
    leetcode题解34-在排序数组中查找元素的第一个和最后一个位置
    查看>>
    leetcode题解347-前 K 个高频元素
    查看>>