消费者/生产者问题:由于消耗缓慢而暂停生产

时间:2018-10-04 17:02:32

标签: java concurrency producer-consumer

我有一个 producer ,可以从磁盘读取文本块。多个消费者正在对该块进行计算。

如果当前要计算的 n 个块更多,我希望生产者暂停从磁盘读取数据。

已将其放在伪代码中以说明我想要实现的目标。

// "produceBlocks" reads blocks from disk one by one
// and feeds them to lambda
produceBlocks(block -> {
  // (!) if activeCounter exceeds a THRESHOLD, then pause

  executorService.submit(() -> { 
     activeCounter.incrementAndGet();

     // do some work

     activeCounter.decrementAndGet();
  });
});

2 个答案:

答案 0 :(得分:3)

我将为线程池使用固定长度的队列,并实现RejectedExecuptionHandler以便在当前线程中运行或暂停并重试。

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/RejectedExecutionHandler.html#rejectedExecution(java.lang.Runnable,%20java.util.concurrent.ThreadPoolExecutor)

例如

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.CallerRunsPolicy.html

这是我有效使用的最后一个选项,并且在配置ExecutorService后不需要额外的代码。

答案 1 :(得分:1)

“如果当前正在计算n个以上的块,我希望生产者暂停从磁盘读取数据。” 实际的任务描述略有不同:生产者在从磁盘读取数据之前应获得许可。。 如果您的生产者是线程,则管理许可证的自然设施是Semaphore。最初它包含n个许可。生产者要读取一个块,请向Semaphore::aquire授予1个许可。消费者处理该块时,消费者使用Semaphore::release释放1个许可。

另一种方法是将区块和许可结合在一起。与从生产者到使用者的输出队列类似,为块创建一个输入阻塞队列。最初放置n个块。生产者要读取一个块,首先要从该队列中取出下一个块。使用者处理完一个块后,将其返回到输入队列。