BlockingQueue:多个生产者如何阻止单个消费者?

时间:2013-11-16 15:39:49

标签: java multithreading producer-consumer

我使用Java' BlockingQueue编写了一个基于生产者/消费者的程序。如果完成所有生产者,我正试图找到一种方法来阻止消费者。有多个生产者,但只有一个消费者。

我为"一个生产者,许多消费者和#34;找到了几种解决方案。场景,例如使用"完成的paket /毒丸" (见this discussion),但我的情况正好相反。

有没有最佳实践解决方案?

3 个答案:

答案 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();
    }

  }
}