【问题标题】:Is this function atomic?这个函数是原子的吗?
【发布时间】:2015-03-17 08:56:50
【问题描述】:

我有一个用例,我需要向底层数据结构添加值并且需要维护顺序。我使用ConcurrentLinkedQueue 作为底层数据结构。以下是函数

public void put(V value) {
   concurrentLinkedQueue.add(value); 
}

这个语句是原子的吗,如果两个线程试图放入值,线程 A 首先(值 V1)和线程 B 第二个(值 V2),是否有可能首先添加 V2,然后再添加 V1。

【问题讨论】:

  • 如果您能准确定义多线程环境意义上的first这个词的含义,我们或许能够回答您的问题?
  • @OldCurmudgeon 首先是调用put 的第一个线程。
  • @user592748 - 如果他们在多线程和多核环境中恰好同时调用会怎样。
  • @user592748 - 然后你应该给你的更新加上时间戳以维持秩序。你永远不应该依赖一种语言的无证特性。
  • @avk,我可以说,这不是一个过程,而是一个方法。实际上,我们有很多名称:例程、子例程、处理程序、过程、方法、函数、子程序、可调用单元,我不知道还有什么。我们如何称呼它取决于我们来自哪里。如果您练习函数式编程,那么您可能会为不改变任何对象状态的命名例程保留“函数”一词。如果您最熟悉任何受 Smalltalk 启发的语言,那么您永远不会说“函数”而总是说“方法”,如果您是 C 程序员,那么它们都是“函数”。

标签: java multithreading thread-safety atomic nonblocking


【解决方案1】:

答案是你的问题有点无意义。

如果两个线程以不同步的方式调用put,那么两者之间没有明显的区别

Thread 1     Thread 2

put          
add
             put
             add

Thread 1    Thread 2

put          
             put
             add
add

即使您使用synchronized 方法,除非您在两个操作之间引入适当的happens-before 关系,否则您的程序中也会出现数据竞争。

问问你自己为什么线程调用put的顺序很重要,并确保通过适当的同步捕捉到这种意图。

【讨论】:

  • 有没有办法在没有阻塞功能的情况下保持订单?您能否详细说明引入适当的发生前关系
  • 事情是这样的:假设线程 1 被触发调用 put,然后很快线程 2 被触发调用 put。但出于纯粹的运气不好,OS/JVM 碰巧调度线程 2 而不是线程 1。这导致线程 2 在线程 1 之前到达 add。现在,你能做些什么呢?正式来说,不多!我能想到的唯一解决方案是尝试使其成为单线程(例如,您可能会使用 nio 包中的选择器)或为您的值添加时间戳,以便它们可以被订购。
  • 问问自己,如果线程 1 在线程 2 之前一纳秒被触发,应该会发生什么?如果您以相反的顺序记录事件,这甚至是“您的错”吗?有没有人可以肯定地告诉你,“嘿,我以相反的顺序向你发送了这两个值!”?如果有人真的可以,他们是如何知道的?消息中是否有固有的时间戳?如果是这样,也请充分利用这一点! :-)
【解决方案2】:

背景:ConcurrentLinkedQueue 在实现中是线程安全的。而且是先进先出。所以它将保持插入顺序。

您的问题的答案:

这个语句是原子的吗

是的

是否有可能先添加V2,然后添加V1

是的

如果您希望线程 A 在线程 B 之前插入值,那么您需要实现例程来显式处理插入顺序。 ConcurrentLinkedQueue 中添加操作的原子性与此无关。这完全取决于哪个线程首先调用 add() 方法。

如果您想保持秩序(即 - FIFO),多线程可能不是您的答案。请问为什么需要多线程执行?我可以想到几种情况,您需要使用 FIFO 顺序进行多线程处理。仅仅因为您试图并行处理有序数据,而不能保证以相同的顺序完成处理。

如果碰巧绝对需要用户并行处理,您可以使用如下流行的线程配置:

  1. 您可以拥有一个 Manager-worker 系统,在该系统中,经理线程获取作业,并将其委托给工作线程池,该线程池将根据作业数据结构中的某些属性(即- 时间戳、序列号)。

  1. 您可以有一个委托线程来获取作业,并将其委托给一个工作线程池,该线程将执行处理并将结果提交给另一个线程,该线程将根据作业中的某些属性执行重新排序数据结构(即时间戳、序列号)。

在管理器或输出排序器线程中,您可能必须维护数据缓冲区,这些缓冲区将在排序之前保存数据以进行调度。

如您所见,这样的实现可能非常复杂。所以你必须问自己,'你真的需要并行处理数据吗?'。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-15
    • 1970-01-01
    • 2017-06-22
    • 2021-11-20
    相关资源
    最近更新 更多