【问题标题】:Java ArrayList.add() method thread safe for purely parallel adding? [duplicate]纯并行添加的Java ArrayList.add() 方法线程安全吗? [复制]
【发布时间】:2015-02-11 02:10:58
【问题描述】:

考虑一个函数的 for 循环,该函数接受一个 ArrayList 引用并将一个对象添加到该 ArrayList。我现在想并行执行每个函数调用。

如果我不关心添加对象的顺序并且没有函数读取或操作任何 ArrayList 元素,那么 ArrayList.add() 方法是否线程安全? 所以我只想确保在并行调用结束时所有对象都被添加到列表中。

【问题讨论】:

  • 试试Collections.synchronizedList(new ArrayList<Object>())
  • 那你不是在找包吗?它可以包含多个,但顺序无关紧要。我不认为 Java 提供了标准实现,但它可能会在以后的搜索中帮助您。
  • OP 实际需要的是每个线程单独累积其贡献,然后合并来自所有线程的结果。也就是说,他需要Stream.parallel()
  • 就在the documentation 中:"注意这个实现是不同步的。 如果多个线程同时访问ArrayList 实例,并且至少有一个线程修改从结构上看,列表必须在外部同步。” 不是我的重点,粗体和斜体实际上在文档中。文档是您获取信息的第一站。

标签: java multithreading arraylist thread-safety


【解决方案1】:

不,它不是线程安全的。使用Collections.synchronizedList() 包装您的列表,或在访问列表时使用显式同步。

【讨论】:

  • 我想避免同步的开销,因为我不是从 ArrayList 读取...如果两个线程想要同时添加到 ArrayList 会发生什么?
  • 任何事情都可能发生:它可能运行良好,或者元素可能丢失,或者您可能会遇到异常,或者列表可能处于不连贯的状态。让你的程序正确,然后看看它是否足够快。如果不是,并且如果您证明对列表的同步访问是问题(这不太可能),那么请考虑如何优化代码。
  • 如果 OP 得出结论,他会发现没有共享状态模型可以提高他的性能。
  • 如果有人来寻找 addAll(),请注意,不,它也不是线程安全的。
【解决方案2】:

为此,您可以使用CopyOnWriteArrayList,因为ArrayList 不是线程安全的。

CopyOnWriteArrayList

编辑: 我只是引用 Oracle 来告诉你,这就是你所需要的:

ArrayList 的线程安全变体,其中所有可变操作(添加、设置等)都是通过制作底层数组的新副本来实现的。 这通常成本太高,但是当遍历操作的数量大大超过突变时,它可能比替代方法更有效,并且在您不能或不想同步遍历但需要排除并发线程之间的干扰时很有用。

【讨论】:

  • “遍历操作的数量远远超过突变”——这听起来根本不像是对 OP 情况的描述。
【解决方案3】:

不,add 方法使用了许多没有线程安全控制的类级别属性。在多个线程中同时调用 add() 很可能会导致问题。

【讨论】:

    【解决方案4】:

    ArrayList.add() 不是线程安全的。即使您没有从其他线程读取列表,也不应该依赖这个逻辑假设。这是ArrayList.add()的定义:

    public boolean add(E e) {
        ensureCapacity(size + 1);
        elementData[size++] = e;
        return true;
    }
    

    作为一个在没有同步的情况下可能出现的问题的示例,size 属性在添加所有元素后可能会不一致。如果稍后尝试获取元素的数量,结果可能不正确。

    【讨论】:

    • 这实际上低估了没有同步的问题的全部潜力。即使代码看起来是原子的,它仍然会在没有同步的情况下被破坏。
    • @MarkoTopolnik 这只是一个简单的例子。
    【解决方案5】:

    不,它不是线程安全的。您有两个选择:

    1. 使用不同的集合:CopyOnWriteArrayList
    2. 在多线程环境中修改数组列表时使用显式同步。

    【讨论】:

      【解决方案6】:

      它不是线程安全的。在没有同步的情况下,线程可以践踏彼此对列表的修改,甚至可能看不到彼此对列表的更改。无论哪种方式,它都会被破坏。

      您应该首先使用 Collections.synchronizedListsynchronized statement 围绕 add 调用使​​代码工作。

      如果你发现每个操作的同步开销太大,下一个选择是给每个线程自己的私有 ArrayList,然后定期,或者在最后,将私有 ArrayLists 的内容转储到主 ArrayList .

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-08
        • 2013-03-31
        • 1970-01-01
        • 2017-07-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多