`ArrayList :: get`线程安全吗?

时间:2017-10-06 21:58:30

标签: java multithreading arraylist thread-safety

我了解修改ArrayList使其不是thread-safe

➠但是如果ArrayList 被修改,可能受Collections.unmodifiableList调用的保护,则调用ArrayList::get线程安全吗?

例如,是否可以将ArrayList传递给Java Stream以对其元素进行并行处理?

3 个答案:

答案 0 :(得分:2)

  

但是如果未修改ArrayList则调用ArrayList :: get thread-safe?

不,它不是线程安全的。

如果您执行以下操作,则会出现问题:

  1. 线程A创建并填充列表。
  2. 线程A将对列表的引用传递给线程B(在关系之前没有发生
  3. 主题B在列表中调用get
  4. 除非在链之间发生正确的发生在1和3之间,否则线程B可能会在某些工作负载下的某些平台上看到过时的值......

    有办法解决这个问题。例如,如果线程A在步骤1之后启动线程B,则之前会发生。类似地,如果A通过正确同步的setter / getter调用或volatile变量将列表引用传递给B,则会发生此事。

    但最重要的是(仅)不更改列表不足以使其成为线程安全的。

      

    ...可能受到对Collections.unmodifiableList

    的调用的保护

    Collections.unmodifiableList的创建应该提供关系之前发生...,前提是您通过包装器访问列表而不是直接通过ArrayList::get

      

    例如,可以将ArrayList传递给Java Stream以对其元素进行并行处理吗?

    这是一个特定的情况。 Stream机制将在关系之前提供。如果按预期使用。这很复杂。

    这来自Spliterator界面javadoc

      

    "尽管它们在并行算法中具有明显的实用性,但预计分裂器不是线程安全的。相反,使用分裂器的并行算法的实现应确保分裂器一次仅由一个线程使用。这通常很容易通过串行线程限制来实现,这通常是通过递归分解工作的典型并行算法的自然结果。调用trySplit()的线程可以将返回的Spliterator移交给另一个线程,该线程又可以遍历或进一步拆分该Spliterator。如果两个或多个线程在同一个分裂器上并发运行,则拆分和遍历的行为是不确定的。如果原始线程将分裂器移交给另一个线程进行处理,最好是在使用tryAdvance()消耗任何元素之前进行切换,因为某些保证(例如SIZED分裂器的estimateSize()的准确性)仅有效在遍历开始之前。"

    换句话说,线程安全是Spliterator实现和Stream实现的共同责任。

    考虑到这一点的简单方法是"魔术发生" ...因为如果它没有,那么并行流将无法使用。

    但请注意,Spliterator根本不一定使用ArrayList::get

答案 1 :(得分:0)

线程安全只是一个问题,正如您所说,当线程之间的值可以更改时。如果没有添加或删除元素,则对象保持不变,并且所有线程都可以轻松地对其进行操作。对于Java中的大多数对象,这都是相同的。

你可以通过线程添加到一个ArrayList,如here所示,但我不会在它上面。

答案 2 :(得分:0)

不,ArrayList.get()本身并不是线程安全的,因为它不会修改List。您仍需要一些东西来创建每个get() 修改列表的每个方法调用之间的先发生关系。

然而,假设您首先实例化并填充列表,然后执行多个get(),从不再次修改它,或者至少在所有get()之后的某个同步点之后再修改它。然后,您不需要在各种get()之间进行相互同步,并且您可以在get()和初始化阶段结束之间获得廉价的同步。这实际上是您将提供的非共享List作为并行流计算的输入的情况。