从BlockingQueue获取时缺少的项目

时间:2014-03-12 22:32:41

标签: java multithreading concurrency queue executorservice

我有一个海量数据集,我需要填充到数据库中。我正在编写一个基于Java Concurrency Library的代码(生产者 - 使用BlockingQueue和executorService的消费者模型),它可以在数据到达时不断向队列添加数据。消费者不断检索数据,除非遇到“毒药”(然后它就会死亡)。

主类,要发布虚拟数据。队列大小有意保持较小:

public class MessageProcessor {
private static final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(
        5, true);
private static final ExecutorService executor = Executors
        .newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static final ExecutorService consumerExecutor = Executors
        .newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static final String POISON = "THE_END";


public void processMessages() throws InterruptedException {

//Create and start consumer 
    Runnable consumer = new MessageConsumer(queue);
    consumerExecutor.execute(consumer);

    for (String payload : getPayload()) {
        //create and start producer with given payload
        Runnable producer = new MessageProducer(queue, payload);
        executor.execute(producer);
    }

    executor.shutdown();
    executor.awaitTermination(1, TimeUnit.MINUTES);

    consumerExecutor.shutdown();
    consumerExecutor.awaitTermination(1, TimeUnit.MINUTES);

}

private List<String> getPayload() {
    List<String> payloads = new ArrayList<>();
    payloads.add("data1");
    payloads.add("data2");
    payloads.add("data3");
    payloads.add("data4");
    payloads.add("data5");
    payloads.add("data6");
    payloads.add("data7");
    payloads.add("data8");
    payloads.add("data9");
    payloads.add("data10");
    payloads.add(POISON);

    return payloads;
}}

制片人Runnable:

public class MessageProducer implements Runnable {
private BlockingQueue<String> queue;
private String payload;

public MessageProducer(BlockingQueue<String> queue, String payload) {
    this();
    this.queue = queue;
    this.payload = payload;
}

private MessageProducer() {
}

public void run() {
    try {
            queue.put(payload);
            System.out.println("Put : " + payload );
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}}

Consumer Runnable:

public class MessageConsumer implements Runnable {

private BlockingQueue<String> queue;
private static final String POISON = "THE_END";

public MessageConsumer(BlockingQueue<String> queue) {
    this();
    this.queue = queue;
}

private MessageConsumer() {
}

public void run() {

    String payload = "";
    do {
        try {
            payload = queue.take();
            System.out.println("Got : " + payload );
        } catch (InterruptedException ie) {
            // handle
            break;
        }
    } while (!payload.equals(POISON));
}}

输出:

Put : data1
Put : data2
Put : data3
Put : data7
Put : data6
Put : data5
Got : data1
Got : data2
Got : data3
Got : data5
Put : data10
Put : data8
Put : data9
Got : data6
Got : data7
Put : data4
Put : THE_END
Got : data8
Got : data9
Got : data10
Got : THE_END

当我执行新的MessageProcessor.processMessages()时,我发现两个异常:

  1. 消费者无法获取项目:data4(我假设是因为它可以在检索到“data4”之前获取有毒数据(“THE_END”)) - 但为什么它不能从队列中插入的顺序获取数据哪个是FIFO?
  2. 队列(put)中的插入不会按列表中的项目顺序发生(例如,在插入“data3”,“data7”之后)
  3. 谢谢!

3 个答案:

答案 0 :(得分:2)

你的两个问题是一样的。

由于您有多个并行运行的生成器,因此您无法保证第一个生成器在第二个生成器之前将其元素放入队列。因此,这些项目不会在队列中排序,并且毒物在data4之前出现,因此不会被消费者使用。

答案 1 :(得分:1)

在队列中放置条目是不确定的,因为您为每条消息使用唯一的runnable,而不是在单个线程中顺序循环。

正如你所说,这可能解释了为什么有些消息没有被看到,因为它们会在结束之后出现。

答案 2 :(得分:1)

你的队列是一个fifo,是的,但是你并没有按照fifo顺序将数据推送到队列中。

如果.availableProcessors()返回&gt; 1,您有几个生产者将数据推送到队列中 - 您执行者管理的线程无法保证按照您调用executor.execute(producer);的顺序顺序运行。