ArrayBlockingQueue,如果队列已满,则在删除队列头时添加元素

时间:2017-12-18 14:59:56

标签: java multithreading java-8

我正在尝试编写一个像ArrayBlockingQueue这样的简单队列,如果在添加元素时队列已满,则会删除队列的头部。该课程应该只有以下公共方法

  • 获取队列的大小
  • 从队列的头部获取元素。如果没有元素可用阻止。
  • 在队列尾部添加元素

有人可以查看下面的代码,如果有更好的方法可以告诉我吗?

public class CircularArrayNonBlockingQueue<E> {
    private ArrayBlockingQueue<E> blockingQueue;

    public CircularArrayNonBlockingQueue(int size) {
        blockingQueue = new ArrayBlockingQueue<>(size);
    }

    public synchronized int size() {
        return blockingQueue.size();
    }

    public synchronized void add(E element) {
        if(blockingQueue.remainingCapacity() <= 0) {
            blockingQueue.poll();
        }
        blockingQueue.add(element);
    }

    public synchronized E poll() {
        return blockingQueue.poll();
    }
}

修改 根据评论中的讨论,我不需要制作所有方法synchronized。更新后的代码如下所示 -

public class CircularNonBlockingQueue<E> {
    private final ArrayBlockingQueue<E> blockingQueue;

    public CircularNonBlockingQueue(int size) {
        blockingQueue = new ArrayBlockingQueue<>(size);
    }

    public int size() {
        return blockingQueue.size();
    }

    public synchronized void add(E element) {
        if(blockingQueue.remainingCapacity() <= 0) {
            blockingQueue.poll();
        }
        blockingQueue.add(element);
    }

    public E take() throws InterruptedException {
        return blockingQueue.take();
    }
}

1 个答案:

答案 0 :(得分:2)

拥有线程安全的后端集合并不一定能够生成正确的程序。当只有add方法为synchronized时,take()方法可能会同时运行,因此在if(blockingQueue.remainingCapacity() <= 0) add内进行take()测试后,同时运行poll()会删除元素,因此add中的add()可能会不必要地删除元素。 take()add之前完成的情况存在可察觉的差异,因为消费线程将收到不同的项目。换句话说,效果就好像synchronized有时不会删除最旧的项目,而是第二个最旧的项目。

另一方面,如果对所有方法始终使用import java.util.ArrayDeque; public class CircularBlockingQueue<E> { private final ArrayDeque<E> blockingQueue; private final int maxSize; public CircularBlockingQueue(int size) { if(size<1) throw new IllegalArgumentException("size == "+size); blockingQueue = new ArrayDeque<>(size); maxSize = size; } public synchronized int size() { return blockingQueue.size(); } public synchronized void add(E element) { if(blockingQueue.size() == maxSize) { blockingQueue.poll(); } blockingQueue.add(element); notify(); } public synchronized E take() throws InterruptedException { while(blockingQueue.isEmpty()) wait(); return blockingQueue.remove(); } } ,则不需要具有线程安全的后端集合:

BlockingQueue

但是,如果您可以使用关于最旧元素的较弱保证,则可以使用synchronized而不需要任何public class CircularBlockingQueue<E> { private final ArrayBlockingQueue<E> blockingQueue; public CircularBlockingQueue(int size) { blockingQueue = new ArrayBlockingQueue<>(size); } public int size() { return blockingQueue.size(); } public void add(E element) { while(!blockingQueue.offer(element)) { blockingQueue.poll(); } } public E take() throws InterruptedException { return blockingQueue.take(); } }

take()

必须注意的是,这些解决方案都没有提供“公平性”。因此,如果生产者和消费者线程的数量与队列的容量相比较大,那么生产者可能会重复删除项目而不重新激活$(".item").attr("data-groups", function() { return $(this).find('.caption p span').attr('class'); }); 中阻止的线程。因此,您应始终确保拥有足够大的容量。