对于写入固定大小数组的不同部分的并行线程,是不是存在线程安全的 Java 数据结构?

Posted

技术标签:

【中文标题】对于写入固定大小数组的不同部分的并行线程,是不是存在线程安全的 Java 数据结构?【英文标题】:Is there a Java data structure that is thread-safe for parallel threads writing to different parts of an array of fixed size?对于写入固定大小数组的不同部分的并行线程,是否存在线程安全的 Java 数据结构? 【发布时间】:2015-05-11 15:10:46 【问题描述】:

这就是我想要实现的:

固定大小的(单例)数组(比如 1000 个元素)

线程池并行将较小的 (

我们保证池中所有线程的总写入量将写入

写入顺序无关紧要,但它们必须是连续的,例如线程 1 填充数组索引 0-49,线程 3 索引 50-149,线程 2 索引 149-200

是否有线程安全的数据结构来实现这一点?

显然,我需要同步“索引管理器”,它分配给定线程需要写入的数组索引中的位置。但是数组本身是否有一个 Java 数据结构可以用于此,而不用担心线程安全?

【问题讨论】:

注意:基于this,我认为常规数组应该可以工作,但我是一个 Java 新手,无法确定 因此,写入数组索引提供了与写入非易失性字段相同的内存语义。如果您正在寻找真正的先发生顺序,您将丢失它写入单个数组。 @JohnVint- 在这种情况下不关心真正的发生之前。我总是(通过应用程序语义)确保在数据写入之前我不会读取数据。 在这种情况下,您对线程安全的担忧应该与写入非易失性字段相同。如果您认为可以这样做,那么写入数组也应该没问题。 如果线程总是要在固定索引上写入,那么 IMO 就不需要担心线程安全。请纠正我我错了。 【参考方案1】:

您应该可以使用AtomicReferenceArray。您可以使用compareAndSet 安全地更新索引或自动更新(尽管看起来您不需要这样做)。

编辑以解决 akhil_mittal 的问题。

让我们将思路从更新数组转换为更新单个字段。如果您要更新类中的字段,则写入将不会发生字撕裂,写入将不会是来自一个线程的一些位和来自另一个线程的一些位。数组索引也是如此。

但是,如果您要通过多个线程更新类中的字段,则来自一个线程的写入可能不会立即对另一个线程可见。这是因为写入可能会在处理器缓存上缓冲并最终刷新到其他处理器。对于写入特定索引的数组也是如此。它最终是可见的,但不保证发生前的顺序。

我们还需要关心线程安全吗

您需要担心线程安全,就像您需要担心非易失字段的线程安全一样。事实证明,DVK 可能不需要担心写入会立即可见。

这个答案的重点是解释数组写入不一定是线程安全的,使用 AtomicReferenceArray 可以保护您免受延迟写入。

【讨论】:

如果所有线程都将写入固定的连续位置,我们还需要关注线程安全吗?请澄清:) 可能有点矫枉过正。 OP 只询问 writing 到数组。没有提及同时读取数组的其他线程。如果目标只是使用 N 个线程来初始化数组,并且如果在初始化完成之前没有线程会读取数组,那么唯一需要同步的地方就是最后,当 (并确保)所有初始化工作人员都已完成。 AtomicXxxxxxxArray 将同步每次写入,这会减慢速度。 @jameslarge 你是对的,它可能是。如果您有兴趣将其发布为答案,我认为这是合理的。根据 OP 的评论,这似乎也是正确答案。【参考方案2】:

其他人已经回答了你的问题,所以我只是添加示例:

    由不同线程添加到数组是并行排序的工作方式。 使用 Fork/Join 框架创建数组是通过工作线程写入数组的不同部分来实现的。

去做吧,你很好。

【讨论】:

使用 Fork/Join 框架创建数组是通过工作线程写入数组的不同部分来实现的。 这是因为所有线程在加入时同步 fork/join 方面。只是想避免混淆为什么 ForkJoin 有效以及为什么我建议使用 AtomicReferenceArray。

以上是关于对于写入固定大小数组的不同部分的并行线程,是不是存在线程安全的 Java 数据结构?的主要内容,如果未能解决你的问题,请参考以下文章

我可以从多个进程/线程写入 HDF5 文件吗?

mfc 对话框 改变大小

在C ++中写入相同值的Race Condition?

C++:在多个线程中访问同一数组/向量的不同单元是不是会产生数据竞争?

每次将大约固定大小的字节数组发送到另一个方法

如果我编写一段代码,其中每个线程修改数组的完全不同部分,那会保持缓存一致性吗?