serah
serah

Reputation: 2117

Alternative to ConcurrentLinkedQueue, do I need to use LinkedList with locks?

i am currently using a ConcurrentLinkedQueue, so that I can use natural order FIFO and also use it in a thread safe application . I have a requirement to log the size of the queue every minute and given that this collection does not guarantee size and also cost to calculate size is O(N), is there any alternative bounded non blocking concurrent queue that I can use where in obtaining size will not be a costly operation and at the same time the add/remove operation is not expensive either?

If there is no collection, do I need to use LinkedList with locks?

Upvotes: 3

Views: 727

Answers (1)

Eugene
Eugene

Reputation: 120968

If you really (REALLY) need to log a correct, current size of the Queue you are currently dealing with - you need to block. There is simply no other way. You can think that maintaining a separate LongAdder field might help, may be making your own interface as a wrapper around ConcurrentLinkedQueue, something like:

interface KnownSizeQueue<T> {
    T poll();
    long size();
}

And an implementation:

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

I just encourage you to add one more method, like remove into the interface and try to reason about the code. You will, very shortly realize, that such implementations will still give you a wrong result. So, do not do it.

The only reliable way to get the size, if you really need it, is to block for each operation. This comes at a high price, because ConcurrentLinkedQueue is documented as:

This implementation employs an efficient non-blocking...

You will lose those properties, but if that is a hard requirement that does not care about that, you could write your own:

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

Or, of course, you can use an already existing structure, like LinkedBlockingDeque or ArrayBlockingQueue, etc - depending on what you need.

Upvotes: 1

Related Questions