我正在尝试编写一个像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();
}
}
答案 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');
});
中阻止的线程。因此,您应始终确保拥有足够大的容量。