我有一个生产者/消费者模式,如下所示
每个生产者都在运行数据库查询并将结果写入其队列。消费者轮询所有生产者队列。目前,如果存在数据库错误,生产者线程将死亡,然后消费者将永远停留在产品队列上等待更多结果。
我应该如何构建它以正确处理捕获错误?
答案 0 :(得分:4)
我曾经做过类似的事情,并决定使用一个濒临死亡的生产者线程将从catch-block推入队列的sentinel值。您可以推送异常本身(这在大多数情况下都适用),或者具有特殊对象。无论如何,将异常推送到消费者以进行调试是非常好的。
答案 1 :(得分:1)
似乎生产者死亡时唯一的选择是停止消费者。
要做到这一点,你可以使用毒丸。这是一个特殊的对象,生产者在停止时添加,消费者知道在收到它时停止。毒丸可以添加到finally
区块中,因此无论生产者如何被杀死/死亡,它都会被添加。
鉴于您只有一个消费者,我会使用一个队列。这样,您的消费者只会阻止所有生产者死亡的地方。
答案 2 :(得分:1)
无论你实际上是什么类推入队列,它都应该包含成功/失败/错误成员,以便消费者可以检查失败。
Peter已经建议只使用一个队列 - 我没有看到如何避免所有轮询应该是任何特定问题 - 队列中的对象可以有成员来识别它们来自哪个生产者,以及任何其他元数据,如果必需的。
答案 3 :(得分:0)
如果队列中的元素不再存在一段时间,您可能会添加一些超时来终止消费者。
另一种方法可能是让生产者保持一个“活着”的标志,并通过将其设置为假来表明他们正在死亡。如果生产者连续运行但可能并不总是从数据库中获得结果,那么“生命”标志可能是生产者最后一次报告生存的时间,然后使用超时来检查生产者是否已经死亡(当上次报告时)活着很久以前)。
答案 4 :(得分:0)
回答我自己的问题。
我使用了以下课程。它需要一个Runnable列表并且并行执行它们,如果一个失败,它会中断所有其他的。然后我在生产者和消费者中断处理,在被打断时优雅地死去。
这适用于我的情况。
感谢所有评论/答案,因为他们给了我一些想法。
// helper class that does the following
//
// if any thread has an exception then interrupt all the others with an eye to cancelling them
// if the thread calling execute() is interrupted then interrupt all the child threads
public class LinkedExecutor
{
private final Collection<Runnable> runnables;
private final String name;
public LinkedExecutor( String name, Collection<Runnable> runnables )
{
this.runnables = runnables;
this.name = name;
}
public void execute()
{
ExecutorService executorService = Executors.newCachedThreadPool( ConfigurableThreadFactory.newWithPrefix( name ) );
// use a completion service to poll the results
CompletionService<Object> completionService = new ExecutorCompletionService<Object>( executorService );
for ( Runnable runnable : runnables )
{
completionService.submit( runnable, null );
}
try
{
for ( int i = 0; i < runnables.size(); i++ )
{
Future<?> future = completionService.take();
future.get();
}
}
catch ( InterruptedException e )
{
// on an interruption of this thread interrupt all sub-threads in the executor
executorService.shutdownNow();
throw new RuntimeException( "Executor '" + name + "' interrupted", e );
}
catch ( ExecutionException e )
{
// on a failure of any of the sub-threads interrupt all the threads
executorService.shutdownNow();
throw new RuntimeException( "Execution execution in executor '" + name + "'", e );
}
}
}