【问题标题】:Java: Thread producer consumer what is the most efficient way to wait for data to be producedJava:线程生产者消费者等待数据产生的最有效方式是什么
【发布时间】:2023-03-08 14:20:02
【问题描述】:

当使用 BlockingQueue 来消费产生的数据时,等待数据出现的最有效方法是什么?

场景:

步骤 1) 数据列表将是一个添加时间戳的数据存储。这些时间戳需要按照最接近当前时间的优先级排序。此列表可能为空。一个线程将在其中插入时间戳。 制作

第 2 步) 我想在另一个线程中使用此处的数据,该线程将从数据中获取时间戳并检查它们是否在当前时间之后。 消费者然后生产

步骤 3) 如果它们在当前时间之后,则将它们发送到另一个线程以供使用和处理。在此处处理时间戳数据后,从步骤 1 数据存储中删除。 使用然后编辑原始列表。

在下面的代码中,数据字段是指步骤 1 中的数据存储。 结果是当前时间之后已发送的时间戳列表。第2步。 然后将在步骤 3 中使用结果。

private BlockingQueue<LocalTime> data;
private final LinkedBlockingQueue<Result> results = new LinkedBlockingQueue<Result>();

@Override
public void run() {
  while (!data.isEmpty()) {
    for (LocalTime dataTime : data) {
      if (new LocalTime().isAfter(dataTime)) {
        results.put(result);
      }
    }
  }
}

问题 等待数据添加到可能为空的数据列表中的最有效方法是什么?关注:

while (!data.isEmpty())

继之前的question.

【问题讨论】:

  • 使用data.take()!=nullisEmpty 调用是非阻塞的。
  • 是的,如果你要轮询,那么使用阻塞队列有什么意义呢?
  • 您的代码显示!data.isEmpty(),这意味着您正在循环直到数据列表中有一些数据(等待数据被使用)。但是您的要求是 - “等待数据被添加在数据列表中可能是空的。”您在这里有什么要求?
  • @hagrawal 我添加了更多上下文
  • 所以你的意思是你想等待从results.put(result);产生数据??我认为这将是最初的案例。一旦产生了一些数据会发生什么。根据第 3 步,您希望它被某个线程消耗,假设产生了 1 个数据,并且它被消耗了,现在同样的线程将再次等待下一个项目的产生?关键是-等待从results.put(result); 生成数据的同一线程还是不同的线程,或者线程将继续消耗,但如果数据不存在,那么它应该等待吗?单线程或多线程部分不清楚。

标签: java multithreading java-7 producer-consumer blockingqueue


【解决方案1】:

等待数据生成的最有效方式是什么

BlockingQueue 具有阻塞功能,它将暂停等待队列非空或未满的线程。在您的情况下,您正在消耗 CPU 的队列上旋转。这不是优选的。

您应该使用take

Suspending 检索并移除此队列的头部,如有必要,等待元素变为可用。

BlockingQueue#take

这将是等待队列中元素的最有效方式,因为挂起的线程不会消耗 CPU。一旦一个新项目被放入队列,等待的线程就会被唤醒。

然后您可以使用put,它与take 具有相同的等待语义,但前提是队列未满。

public void run(){
   LocalTime timestamp = null;
   while((timestamp = data.take()) != null){
      ...
   }
}

根据我们的 cmets 进行更新:

但在这种情况下,时间戳是按顺序创建的,并且 添加。但是未来的时间戳可能会更少。例如。头节点为 2 未来分钟,第二个节点是 1 分钟,所以第二个节点想要 先处理

然后我的跟进:

所以你需要根据 LocalDate 的时间戳进行优先排队吗?

不确定您使用的是 JodaTime 还是 Java 8 中的 LocalDate,我们假设后者。

您可以使用具有相同阻塞语义的PriorityBlockingQueue。但是,BlockingQueue 的优先级方面将根据定义的任何顺序对元素进行排队。在您的情况下,使用 LocalDate 您可以将元素从最老到最年轻或从最年轻到最老排序。

BlockingQueue<LocalDate> data = new PriorityBlockingQueue<>(); 

OR INVERSE THE ORDER

BlockingQueue<LocalDate> data = new PriorityBlockingQueue<>(0, (a,b) -> b.compareTo(a));

在这种情况下,您将按自然顺序处理 LocalDate,而不是按它们入队的顺序。

如果您使用 JodaTime 的 LocalDate,您可能需要实现自己的 Comparator,类似于我的第二个示例。

编辑: 刚刚意识到您已将其标记为 java-7。因此,您将使用 JodaTime,如果 JodaTime LocalDate 没有实现 Comparable,只需创建您自己的。

【讨论】:

  • 谢谢,我认为现在使用 data.peek() 来查看它更合适,因为第一个节点不一定是要处理的节点。
  • 只是好奇,为什么不处理第一个节点?
  • 我认为这很可能在一般意义上被接受。但在这种情况下,时间戳是按顺序创建并添加的。但是未来的时间戳可能会更少。例如。头节点是未来2分钟,第二节点是1分钟,所以第二个节点要先处理。
  • @Stuart 所以你需要根据LocalDate的时间戳优先排队?
  • 感谢您积极主动地提出问题。非常好的解决方案。
【解决方案2】:

您需要使用take 方法。此方法将在队列为空时阻塞。这将取代您查看队列是否为空的检查。

其次,为什么需要时间戳?如果时间戳是为了确保您按照将请求放入队列的顺序处理请求,那么您不需要它,因为队列是先进先出的,并且是为并发多线程环境而设计的。如果时间戳来自系统外部,一些外部时间戳,其中请求可能乱序但需要按顺序处理,则此 BlockQueue 不会将其截断。您可能需要PriorityBlockingQueue,您将在其中按时间戳对请求进行优先级排序。所以要么去掉时间戳,要么使用PriorityBlockingQueue

【讨论】:

    猜你喜欢
    • 2021-02-16
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    相关资源
    最近更新 更多