【问题标题】:Producer/Consumer - producer adds data to collection without blocking, consumer consumes data from collection in batch生产者/消费者 - 生产者将数据添加到集合而不阻塞,消费者批量消费来自集合的数据
【发布时间】:2014-04-08 07:13:52
【问题描述】:

我有一个有点不寻常的生产者/消费者用例。 我有一些生产者的真实世界用例,我希望他们能够将对象添加到集合中而不会阻塞。 消费者(只有一个)应该阻塞,直到集合中有一定数量的对象可用(例如 500),然后批量消费它们。虽然少于 500 个,但它应该阻塞并等待集合填满。我不介意队列是否在短时间内超过此值(700、1000 等)。

我目前似乎没有找到解决这个确切问题的解决方案。 我正在考虑使用 ConcurrentLinkedQueue 并让消费者定期检查队列是否有足够的数据,但这似乎适得其反。

另一个想法是使用 LinkedBlockingQueue。生产者不会阻塞(除非队列已满,这意味着它具有 Integer.MAX_VALUE 值 - 这不是我的情况,所以一切都很好)。消费者将执行 queue.take() 并将元素添加到内部集合中。当内部集合达到 500 个元素时,它将批量消耗它们。

你有什么建议吗?

谢谢!

【问题讨论】:

    标签: java collections concurrency queue producer-consumer


    【解决方案1】:

    最后我选择了 LinkedBlockingQueue。

    生产者将项目添加到队列中。

    消费者将执行 queue.poll() 并将项目保存到内部集合中。当内部集合达到 500 个元素时,它将批量消耗它们。我还可以设置一些超时。例如,如果 X 秒过去了,我将使用该集合,即使它的项目少于所需的项目(例如 220)。

    【讨论】:

    • 为什么不决定批量生产呢?只是好奇什么最适合类似的用例。
    【解决方案2】:

    你可以简单地引入一个中间队列和消费者/生产者,它会从生产者填充的队列中获取项目,将它们存储在一个列表中,一旦列表大小为 500,将列表本身放入队列中读取由消费者。消费者会一直阻塞,直到下一个 500 项列表可用。

    当然,您也可以将此逻辑封装在生产者共享的对象中:生产者将他们的项目添加到“Batcher”对象中。批处理器对象将项目添加到私有列表中,一旦列表大小为 500,批处理器将列表添加到队列中。只要确保批处理器的 addItem() 方法是同步的。这将避免额外的线程和队列。

    【讨论】:

    • 如果 Batcher 的 addItem() 是同步的,它会给想要同时添加对象的生产者造成瓶颈,对吗?这将违背我的目的:“生产者将数据添加到集合中而不会阻塞”。如果 Batcher 将列表添加到队列中,那么消费者将在队列中正确接收这些项目 - 这不是我们想要的,我们希望它一次接收 500 个。如果 Batcher 有一个用于生产者的队列,并且当队列包含 500 个元素时将其排入列表并将其传递给消费者,那不是更好吗...?
    • 将项目添加到共享队列也涉及同步:队列必须保持其完整性。批处理器的 addItem() 方法会增加一个非常小的开销:将一个项目添加到一个预先分配的 500 的 ArrayList 中,检查它的大小,如果是 500,则 addAll() 到队列中并清除列表。在尝试它并查看它是否有效之前,我不会假设这个小开销构成瓶颈。毕竟,如果你有 N 个生产者对应 1 个消费者,生产者就不能以高速率生产,因此对批处理器锁的争用机会就更少了。
    • 并重新阅读我的答案:批处理器将 500 个项目的列表添加到队列中。消费者阻塞,直到队列中有 500 个列表可用。也就是说,在消费者中进行批处理也是可能的。但是,如果您改变主意并引入更多消费者,就不会那么容易了。
    • "消费者阻塞直到队列中有 500 个列表可用。"它将如何做到这一点?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-20
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    相关资源
    最近更新 更多