首页 > 解决方案 > ConcurrentLinkedQueue 的替代方案,我是否需要使用带锁的 LinkedList?

问题描述

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

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

标签: javacollectionsjava-8

解决方案


如果您真的(真的)需要记录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();
        }
    }
}

或者,当然,您可以使用已经存在的结构,例如LinkedBlockingDequeorArrayBlockingQueue等​​ - 取决于您的需要。


推荐阅读