如何对队列进行快照以避免无限循环

时间:2017-02-07 21:37:42

标签: java multithreading

我有一个允许从多个线程插入的ConcurrentLinkedQueue但是当我轮询队列时,我在一个函数中执行它并且我轮询直到队列为空。这可能会导致无限循环,因为在轮询时可能会有线程插入队列。 如何创建队列视图并在轮询之前清空它并仍然是线程安全的?

2 个答案:

答案 0 :(得分:1)

我看到的一种方法是使用ConcurrentLinkedDeque并迭代,直到您到达最近添加的项目。您无法使用单端队列执行此操作,因为读取首先查看头部,您需要读取尾部才能找到最后添加的元素。

ConcurrentLinkedDeque的工作方式是调用offer(Object)add(Object)会将项目放在队列的尾部。对poll()的调用将读取队列的头部,如下所示:

// Read direction --->
HEAD -> E1 -> E2 -> E3 = TAIL
// Write direction --->

当您添加更多项目时,尾部将延伸到最后一个元素,但由于我们想要在最后一次看到它时清空队列,我们​​将抓住尾部指针并迭代直到我们到达尾部。然后我们可以让后续迭代处理我们清空队列时添加的内容。我们先peek因为使用poll会删除最后添加的值,因此我们无法确定何时停止删除元素,因为我们的标记会被移除。

ConcurrentLinkedDeque<Object> deque = new ConcurrentLinkedDeque<>();

public void emptyCurrentView() {
    Object tail = deque.peekLast();
    if (tail != null) {
        while (true) {
            // Poll the current head
            Object current = deque.poll();

            // Process the element
            process(current);

            // If we finish processing the marker
            // Exit the method
            if (current == tail) {
                return;
            }
        }
    }
}

您不需要修改生产者代码,因为生产者的默认offer(Object)add(Object)与将元素添加到尾部完全相同。

答案 1 :(得分:0)

  

如何在轮询之前创建队列视图并将其清空,并且仍然是线程安全的?

是的,这听起来像是一个非常糟糕的模式。使用并发队列实现的重点是您可以同时添加和删除队列。如果你想坚持使用ConcurrentLinkedQueue那么我就是这样做的:

// run every so often
while (true) {
    // returns null immediately if the queue is empty
    Item item = blockingQueue.poll();
    if (item == null) {
       break;
    }
    // process the item...
}

但是,我会考虑改为使用LinkedBlockingQueue,因为它支持take()。消费者线程将处于这样的循环中:

private final BlockingQueue<Item> blockingQueue = new LinkedBlockingQueue<>();
...
while (!Thread.currentThread().isInterrupted()) {
    // wait for the queue to get an item
    Item item = blockingQueue.take();
    // process item...
}

BlockingQueue扩展了Queue,因此poll()循环也可用。