我目前正在使用 ConcurrentLinkedQueue,以便我可以使用自然顺序 FIFO 并在线程安全应用程序中使用它。我需要每分钟记录一次队列的大小,并且鉴于此集合不保证大小并且计算大小的成本也是 O(N),是否有任何替代的有界非阻塞并发队列可以在获取时使用size 不会是一个昂贵的操作,同时添加/删除操作也不昂贵?
如果没有集合,是否需要使用带锁的LinkedList?
答案 0 :(得分:1)
如果您真的(真的)需要记录当前正在处理的 Queue
的正确当前大小 - 您需要阻止。根本没有其他办法。您可以认为维护一个单独的 LongAdder
字段可能会有所帮助,可能将您自己的界面作为 ConcurrentLinkedQueue
的包装器,例如:
interface KnownSizeQueue<T> {
T poll();
long size();
}
还有一个实现:
static class ConcurrentKnownSizeQueue<T> implements KnownSizeQueue<T> {
private final ConcurrentLinkedQueue<T> queue = new ConcurrentLinkedQueue<>();
private final LongAdder currentSize = new LongAdder();
@Override
public T poll() {
T result = queue.poll();
if(result != null){
currentSize.decrement();
}
return result;
}
@Override
public long size() {
return currentSize.sum();
}
}
我只是鼓励您在界面中再添加一种方法,例如 remove
,并尝试对代码进行推理。你很快就会意识到,这样的实现仍然会给你一个错误的结果。所以,不要这样做。
获取大小的唯一可靠方法,如果你真的需要它,就是为每个操作阻塞。这是代价高昂的,因为 ConcurrentLinkedQueue
被记录为:
此实现采用了高效的非阻塞...
您将失去这些属性,但如果这是一个不关心它的硬性要求,您可以编写自己的:
static class ParallelKnownSizeQueue<T> implements KnownSizeQueue<T> {
private final Queue<T> queue = new ArrayDeque<>();
private final ReentrantLock lock = new ReentrantLock();
@Override
public T poll() {
try {
lock.lock();
return queue.poll();
} finally {
lock.unlock();
}
}
@Override
public long size() {
try {
lock.lock();
ConcurrentLinkedQueue
return queue.size();
} finally {
lock.unlock();
}
}
}
或者,当然,您可以使用现有的结构,例如 LinkedBlockingDeque
或 ArrayBlockingQueue
等 - 取决于您的需要。