ConcurrentLinkedQueue 的替代方案,我是否需要使用带锁的 LinkedList?

时间:2021-01-17 13:51:16

标签: java collections java-8

我目前正在使用 ConcurrentLinkedQueue,以便我可以使用自然顺序 FIFO 并在线程安全应用程序中使用它。我需要每分钟记录一次队列的大小,并且鉴于此集合不保证大小并且计算大小的成本也是 O(N),是否有任何替代的有界非阻塞并发队列可以在获取时使用size 不会是一个昂贵的操作,同时添加/删除操作也不昂贵?

如果没有集合,是否需要使用带锁的LinkedList?

1 个答案:

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

或者,当然,您可以使用现有的结构,例如 LinkedBlockingDequeArrayBlockingQueue 等 - 取决于您的需要。