Java:线程生产者消费者等待生成数据的最有效方法是什么

时间:2015-07-24 11:06:41

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

使用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.

2 个答案:

答案 0 :(得分:5)

  

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

BlockingQueue具有阻塞功能,该功能将挂起等待队列不为空或未满的线程。在你的情况下,你在消耗CPU的队列上旋转。这不是优选的。

您应该使用take

  

挂起检索并删除此队列的头部,必要时等待,直到元素可用为止。

BlockingQueue#take

这将是等待队列中元素的最有效方法,因为挂起线程不使用cpu。将新项目放入队列后,等待的线程将被唤醒。

然后您可以使用与put具有相同等待语义的take,但仅限于队列未满。

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

根据我们的评论进行更新:

  

但在这种情况下,时间戳是按顺序创建的   添加。但是未来的时间戳可能会更少。例如。头节点是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,只需创建自己的。

答案 1 :(得分:0)

您需要使用take方法。当队列为空时,此方法将阻塞。这将替换您的检查以查看队列是否为空。

其次,为什么需要时间戳?如果时间戳是为了确保你处理请求,他命令将它们放入队列,那么你就不需要它,因为队列是FIFO并且是为并发多线程环境做的。如果时间戳来自系统外部,某些外部时间戳,请求可能无序,但需要按顺序处理,则此BlockQueue不会删除它。您可能需要PriorityBlockingQueue,您可以按时间戳优先处理请求。因此,要么删除时间戳,要么使用PriorityBlockingQueue