java中的迭代器类型(弱一致)

Spa*_*tan 6 java multithreading iterator thread-safety

我理解fail-fast(LinkedList)和故障安全(copyonwrite)迭代器,但是弱的一致性仍然是个谜.

文档说它可能反映了底层集合的变化,但不能保证.因此,我认为弱一致性不会创建支持集合的副本.(在并发Map中,它在同一个bucketarray上工作).

我假设如果一个线程A创建了一个迭代器并且经过了一半,那么当线程B将一个项目放到数组开头的桶中时,这个更改对于线程A的迭代器是不可见的.

如果B将该项放到数组的末尾,A就会看到它.

是否可能有一个nosuchelement例外?

如果线程A创建一个迭代器,然后遍历到一个项目X,它有一个下一个项目Y,然后jvm停止线程A并恢复线程B,谁删除Y.这对线程A是否可见(我想是这样,否则并发映射将不会'是线程安全的,但对其迭代器的实现方式一无所知),因为它对线程A不可见,那么它很容易引发异常.

Stu*_*rks 11

包文档中给出了弱一致性的定义.为方便起见,我将引用相关位.关于弱一致的迭代器和分裂器,文档说:java.util.concurrent

  • 他们可以与其他业务同时进行
  • 他们永远不会抛出ConcurrentModificationException
  • 它们可以保证在构造时只存在一次元素,并且可以(但不保证)反映构造后的任何修改.

这没有说明(以及使用"一致"一词可能隐含的内容)是迭代不会导致诸如IndexOutOfBoundsException或之类的错误NoSuchElementException.它也没有说明迭代是否会终止!(我相信它会.)从某种意义上说,这种行为确实是一致的,尽管保证相当薄弱.如果在迭代期间发生修改,则第三个子弹特别明确地不保证迭代可以看到哪些元素.

请考虑以下示例:

    List<String> input = Arrays.asList("a", "b", "c", "d", "e");
    List<String> output = new ArrayList<>();

    Deque<String> deque = new ConcurrentLinkedDeque<>(input);
    for (String s : deque) {
        output.add(s);
        if (s.equals("c")) {
            deque.addFirst("XXX");
            deque.removeLast();
        }
    }
Run Code Online (Sandbox Code Playgroud)

A ConcurrentLinkedDeqeue是具有弱一致迭代语义的集合的示例.此代码对其进行迭代并将看到的每个元素添加到副本中,但在迭代过程中,将对deque进行修改.

如果你试试这个,LinkedList你会得到一个ConcurrentModificationException你期望的.使用a ConcurrentLinkedDeque,输出列表是

    [a, b, c, d]
Run Code Online (Sandbox Code Playgroud)

请注意,在删除"e"之前添加了"XXX",因此输出列表仅反映了迭代期间对输入所做的一些修改.由于迭代在这种情况下从左向右进行,因此不会看到对当前迭代点左侧进行的修改,并且可以看到对当前迭代点右侧的修改.当然,并非所有集合都具有如此简单的迭代顺序.

另请注意,输出不会反映任何时间点发生的输入快照.(如果你想要快照语义,你需要使用类似的东西CopyOnWriteArrayList.)唯一的保证是迭代看到的元素在某个时刻出现在输入中.这是一个非常弱的保证!

然而,它比我称之为不一致的行为更强大.考虑使用索引(而不是Iterator对象)迭代ArrayList的代码:

    List<String> input = Arrays.asList("a", "b", "c", "d", "e");
    List<String> output = new ArrayList<>();

    List<String> arrayList = new ArrayList<>(input);
    for (int i = 0; i < arrayList.size(); i++) {
        String s = arrayList.get(i);
        output.add(s);
        if (i == 2) {                   // <<< MODIFY
            arrayList.add(0, "XXX");
        }
    }
Run Code Online (Sandbox Code Playgroud)

在这种情况下输出是

    [a, b, c, c, d, e]
Run Code Online (Sandbox Code Playgroud)

它重复在输入中只出现一次的元素"c".这显然是一个错误.或者,假设标记为MODIFY的行更改为:

        if (s.equals("c")) {
Run Code Online (Sandbox Code Playgroud)

在这种情况下,循环永远不会终止!您还可以轻松地想象一下,使用索引样式循环,在恰当(错误)时间修改列表将导致的情况IndexOutOfBoundsException.

所以你可以看到,在修改集合时迭代一个集合可能会出现很多问题.弱一致的迭代提供了对重复元素的保证,以及可能发生的各种错误或无限循环."弱点"是它们几乎不能确保在迭代期间观察到哪些元素.

最后,请注意,快速失败弱一致性是Java SE规范中定义和使用的特定术语.官方Java文档中的任何地方都不使用术语"故障安全".因此,我建议不要使用"fail-safe"来描述任何Java集合的并发修改策略.有些人认为"故障安全"与"快速失败"相反,你会看到这种情况发生在互联网上的各种博客和文章中.坦率地说,我认为应该避免这种草率的写作.