繁体   English   中英

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

[英]ArrayBlockingQueue in which queue head is removed if the queue is full while adding an element

我正在尝试编写一个简单的队列,如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();
    }
}

拥有线程安全的后端集合不一定可以创建正确的程序。 当仅synchronized您的add方法时, take()方法可能会并发运行,因此有可能在add执行if(blockingQueue.remainingCapacity() <= 0)测试之后,同时运行的take()会删除一个元素,因此addpoll()可能会不必要地删除元素。 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()阻塞的线程的风险。 因此,您应始终确保具有足够大的容量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM