【问题标题】:Java concurrency - writing to different indexes of the same arrayJava并发 - 写入同一数组的不同索引
【发布时间】:2012-01-23 20:36:26
【问题描述】:

假设我有一个数据数组,2个线程可以安全地同时写入同一个数组的不同索引吗?我担心写入速度,我想将“获取要写入的索引”位与实际写入同步。

我正在编写代码,让我假设 2 个线程不会获得相同的索引。

【问题讨论】:

  • 取决于您使用的数组类的类型。你能提供更多信息吗?

标签: java multithreading concurrency


【解决方案1】:

对于数组中的两个不同索引,适用与两个单独变量相同的规则。

Java 语言规范中的章节"Threads and Locks" 开头声明:

17.4.1 共享变量

[...]

所有实例字段、静态字段和数组元素都存储在堆内存中。 在本章中,我们使用术语变量来指代字段和数组元素

这意味着您可以安全地同时写入两个不同的索引。 但是如果要确保消费者线程看到生产者线程写入的最后一个值,则需要将写入/读取同步到同一索引。

【讨论】:

  • 很棒的信息。这是问题所在:是否有不遵循这部分规范的 java 实现?
  • 不,因为它不能算作 Java 实现,对吧? :)
  • java.util.concurrency 的循环屏障,是否可以在读取整个数组之前同步所有写入线程?
【解决方案2】:

在两个不同的线程中修改两个不同的变量是安全的。修改数组中的两个不同元素可以类比为修改不同内存地址下的两个不同变量,至少就操作系统而言。所以是的,它是安全的。

【讨论】:

    【解决方案3】:

    嗯,是的,这在技术上是正确的,但是这个答案有很多警告,让我很担心告诉你是的。因为虽然您可以写入数组中的两个不同位置,但您不能在不遇到并发问题的情况下做很多其他事情。真正的问题在于,如果你能做到这一点,你接下来会做什么?

    如果您的计数器变量随着数组写入不同位置而移动,您可能会遇到并发问题。当您的数组填满时,您可能有两个线程尝试写入同一位置。如果您可能有一个可以读取正在写入的相同位置的数组读取器,那么您将遇到并发问题。因此,如果您从不打算将其读回,那么除了写入不会做任何事情之外,我认为当您添加阅读器时您会遇到并发问题(这将不得不将您的作者锁定)。那么问题来了,如果您不将线程写入的位置移动到阻止它写入数据的位置?如果你从不移动线程写入的头部,你为什么要使用数组?只需给他们单独的锁存器或他们自己的变量来写入,并真正将它们分开。

    如果没有全面了解你的意图,说“是”可能会导致你陷入危险而不考虑你为什么要做你正在做的事情。

    【讨论】:

    • 他可以有多个“写入器”线程同时写入数组的非重叠部分,一旦全部完成,所有内容都会写入。那么就不需要细粒度的同步了。
    【解决方案4】:

    查看CopyOnWriteArrayList,前提是您可以使用arrayList。从其文档中,

    ArrayList 的线程安全变体,其中所有可变操作 (添加、设置等)是通过制作新的副本来实现的 底层数组。

    这通常成本太高,但可能比其他方法更有效 当遍历操作的数量大大超过突变时,并且在以下情况下很有用 您不能或不想同步遍历,但需要排除 并发线程之间的干扰。

    还有

    CopyOnWriteArrayList 的一个实例表现为一个 List 实现,它 允许多个并发读取,并且读取同时发生 写。这样做的方法是每次都制作一个全新的列表副本 它被改变了。

    读取不会阻塞,并且有效地仅支付易失性读取的成本; 写入不会阻塞读取(反之亦然),但一次只能发生一次写入。

    【讨论】:

    • 你建议他每次写值时都复制整个数据结构?!
    猜你喜欢
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 2012-08-05
    • 2021-11-12
    相关资源
    最近更新 更多