我了解修改ArrayList
使其不是thread-safe。
➠但是如果ArrayList
不被修改,可能受Collections.unmodifiableList
调用的保护,则调用ArrayList::get
线程安全吗?
例如,是否可以将ArrayList
传递给Java Stream以对其元素进行并行处理?
答案 0 :(得分:2)
但是如果未修改ArrayList则调用ArrayList :: get thread-safe?
不,它不是线程安全的。
如果您执行以下操作,则会出现问题:
get
。除非在链之间发生正确的发生在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
作为并行流计算的输入的情况。