我正在使用ArrayBlockingQueue,但有时它会变满并阻止其他对象被添加到它。
我想要做的是删除队列中最旧的对象,然后在ArrayBlockingQueue满时添加另一个对象。我需要ArrayBlockingQueue像Guava EvictingQueue但线程安全。我打算扩展ArrayBlockingQueue并覆盖提供(E e)方法,如下所示:
public class MyArrayBlockingQueue<E> extends ArrayBlockingQueue<E> {
// Size of the queue
private int size;
// Constructor
public MyArrayBlockingQueue(int queueSize) {
super(queueSize);
this.size = queueSize;
}
@Override
synchronized public boolean offer(E e) {
// Is queue full?
if (super.size() == this.size) {
// if queue is full remove element
this.remove();
}
return super.offer(e);
} }
上述方法可以吗?或者有更好的方法吗?
谢谢
答案 0 :(得分:1)
您的MyArrayBlockingQueue
不会覆盖BlockingQueue.offer(E, long, TimeUnit)
或BlockingQueue.poll(long, TImeUnit)
。你真的需要一个队列&#34;阻止&#34;特征?如果不这样做,则可以使用EvictingQueue
创建由Queues.synchronizedQueue(Queue)
支持的线程安全队列:
Queues.synchronizedQueue(EvictingQueue.create(maxSize));
对于驱逐阻塞队列,我发现您提议的实施存在一些问题:
remove()
可能会抛出异常。您的offer
方法标有synchronized
,但poll
,remove
等不是这样,因此另一个线程可能会在调用size()
和{ {1}}。我建议使用remove()
而不是抛出异常。poll()
的调用可能仍会返回offer
(即不是&#34;添加&#34;元素),因为另一种竞争条件是在检查大小和/或删除元素之间减小大小不同的线程添加一个填充队列的元素。我建议使用false
结果的循环,直到返回offer
为止(见下文)。true
,size()
和remove()
都需要锁定,因此在最糟糕的情况下,您的代码会锁定并解锁3次(即使这样,它也可能无法按预期运行以前描述的问题)。我相信以下实施将为您提供所需的信息:
offer(E)
请注意,如果在两次调用public class EvictingBlockingQueue<E> extends ArrayBlockingQueue<E> {
public EvictingBlockingQueue(int capacity) {
super(capacity);
}
@Override
public boolean offer(E e) {
while (!super.offer(e)) poll();
return true;
}
@Override
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
while (!super.offer(e, timeout, unit)) poll();
return true;
}
}
另一个线程之间移除元素,则此实现可能会不必要地删除元素。这似乎是我可以接受的,我并没有真正看到一个实用的方法(super.offer(E)
是包私有的,ArrayBlockingQueue.lock
是一个禁止的包,所以我们不能在那里放置一个实现访问和使用锁等。)。
答案 1 :(得分:0)
当你说“它变满并且阻止添加其他对象”时,这是否意味着确保可以随时添加对象就足够了?如果这是真的,您只需切换到无界队列,例如LinkedBlockingQueue
。但请注意与ArrayBlockingQueue
:
链接队列通常比基于阵列的队列具有更高的吞吐量,但在大多数并发应用程序中具有较低的可预测性能。
您可以找到JDK队列实现的概述here。