我使用Java' BlockingQueue编写了一个基于生产者/消费者的程序。如果完成所有生产者,我正试图找到一种方法来阻止消费者。有多个生产者,但只有一个消费者。
我为"一个生产者,许多消费者和#34;找到了几种解决方案。场景,例如使用"完成的paket /毒丸" (见this discussion),但我的情况正好相反。
有没有最佳实践解决方案?
答案 0 :(得分:2)
最佳实践系统是使用count-down latch。这对你有用更有趣.....
也许每个生产者都应该注册并取消注册消费者,并且当所有生产者都被注销(并且队列为空)时,消费者也可以终止。
答案 1 :(得分:0)
据推测,您的生产者正在同一个VM中的不同线程中工作,并且在完成后退出。我会在循环中的所有生成器上创建另一个调用join()的线程,当它存在该循环时(因为所有生成器线程都已结束),它会通知消费者它是时候退出了。这必须在另一个线程中运行,因为join()调用将阻塞。顺便提一下,如果我理解正确的话,rolfl建议使用倒计时锁存器就会出现问题。
或者,如果生产者是Callables,那么消费者可以在循环中的期货上调用isDone()和isCanceled(),这不会被打包,因此它可以在消费者线程中使用。
答案 2 :(得分:0)
您可以使用以下内容,我使用registerProducer()
和unregisterProducer()
来跟踪生产者。另一种可能的解决方案可以使用WeakReference
s。
值得一提的是,此解决方案不会消耗关闭消费者时已经排队的事件,因此关闭时可能会丢失一些事件。
如果消费者获得中断然后处理它们,你将不得不耗尽队列。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class TestConsumerShutdown {
private static interface SomeEvent {
String getName();
}
private static class Consumer implements Runnable {
private final BlockingQueue<SomeEvent> queue = new ArrayBlockingQueue<>(10);
private final ExecutorService consumerExecutor = Executors.newSingleThreadExecutor();
private final AtomicBoolean isRunning = new AtomicBoolean();
private final AtomicInteger numberProducers = new AtomicInteger(0);
public void startConsumer() {
consumerExecutor.execute(this);
}
public void stopConsumer() {
consumerExecutor.shutdownNow();
try {
consumerExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void registerProducer() {
numberProducers.incrementAndGet();
}
public void unregisterProducer() {
if (numberProducers.decrementAndGet() < 1) {
stopConsumer();
}
}
public void produceEvent(SomeEvent event) throws InterruptedException {
queue.put(event);
}
@Override
public void run() {
if (isRunning.compareAndSet(false, true)) {
try {
while (!Thread.currentThread().isInterrupted()) {
SomeEvent event = queue.take();
System.out.println(event.getName());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println("Consumer stopped.");
isRunning.set(false);
}
}
}
}
public static void main(String[] args) {
final Consumer consumer = new Consumer();
consumer.startConsumer();
final Runnable producerRunnable = new Runnable() {
@Override
public void run() {
final String name = Thread.currentThread().getName();
consumer.registerProducer();
try {
for (int i = 0; i < 10; i++) {
consumer.produceEvent(new SomeEvent() {
@Override
public String getName() {
return name;
}
});
}
System.out.println("Produver " + name + " stopped.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
consumer.unregisterProducer();
}
}
};
List<Thread> producers = new ArrayList<>();
producers.add(new Thread(producerRunnable, "producer-1"));
producers.add(new Thread(producerRunnable, "producer-2"));
producers.add(new Thread(producerRunnable, "producer-3"));
for (Thread t : producers) {
t.start();
}
}
}