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