有界PriorityBlockingQueue

时间:2010-02-26 12:48:05

标签: java collections priority-queue

PriorityBlockingQueue是无限的,但我需要以某种方式约束它。实现这一目标的最佳方法是什么?

有关信息,有限PriorityBlockingQueue将用于ThreadPoolExecutor

注意:通过有界我不想抛出异常,如果发生这种情况,我想将对象放入队列中,然后根据其优先级值剪切它。有什么好方法可以做这件事吗?

9 个答案:

答案 0 :(得分:12)

我实际上不会将其子类化。虽然我现在无法将示例代码放在一起,但我建议使用装饰模式的一个版本。

创建一个新类并实现您感兴趣的类实现的接口:PriorityBlockingQueue。我发现这个类使用了以下接口:

Serializable, Iterable<E>, Collection<E>, BlockingQueue<E>, Queue<E>

在类的构造函数中,接受PriorityBlockingQueue作为构造函数参数。

然后通过PriorityblockingQueue的实例实现接口所需的所有方法。添加使其成为有界所需的任何代码。

这是我在Violet UML中汇总的快速图表:

BoundedPriorityblockingQueue class diagram http://theopensourceu.com/wp-content/uploads/2010/03/BoundedPriorityBlockingQueue.png

答案 1 :(得分:5)

Google Collections/Guava库中有一个实现:MinMaxPriorityQueue

  

可以使用最大大小配置最小 - 最大优先级队列。如果是这样,   每次队列的大小超过该值时,队列   根据比较器自动删除其最大元素   (可能是刚刚添加的元素)。这是不同的   来自传统的有界队列,阻止或拒绝新的   满满的元素。

答案 2 :(得分:2)

在我的头脑中,我将它子类化并覆盖put方法来强制执行此操作。如果它结束则抛出异常或做任何看似合适的事情。

类似的东西:

public class LimitedPBQ extends PriorityBlockingQueue {

    private int maxItems;
    public LimitedPBQ(int maxItems){
        this.maxItems = maxItems;
    }

    @Override
    public boolean offer(Object e) {
        boolean success = super.offer(e);
        if(!success){
            return false;
        } else if (this.size()>maxItems){
            // Need to drop last item in queue
            // The array is not guaranteed to be in order, 
            // so you should sort it to be sure, even though Sun's Java 6 
            // version will return it in order
            this.remove(this.toArray()[this.size()-1]);
        }
        return true;
    }
}

编辑:添加和放置调用商品,因此覆盖它应该足够了

编辑2:如果超过maxItems,现在应该删除最后一个元素。虽然可能有更优雅的方式。

答案 3 :(得分:2)

根据Frank V的建议实现BoundedPriorityBlockingQueue之后,我意识到它并没有完全符合我的要求。 主要问题是我必须插入队列的项目可能比队列中已有的项目具有更高的优先级。 因此我真正想要的是一个'pivot'方法,如果我将一个对象放入队列,当队列已满时,我想找回最低优先级的对象,而不是阻塞。

为了充实Frank V的建议,我使用了以下片段......

public class BoundedPriorityBlockingQueue<E> 
   implements 
     Serializable, 
     Iterable<E>, 
     Collection<E>, 
     BlockingQueue<E>, 
     Queue<E>, 
     InstrumentedQueue 
{

...     私人最终的ReentrantLock锁定; // = new ReentrantLock();     private final Condition notFull;

final private int capacity;
final private PriorityBlockingQueue<E> queue;

public BoundedPriorityBlockingQueue(int capacity) 
  throws IllegalArgumentException, 
         NoSuchFieldException, 
         IllegalAccessException 
{
   if (capacity < 1) throw 
       new IllegalArgumentException("capacity must be greater than zero");      
   this.capacity = capacity;
   this.queue = new PriorityBlockingQueue<E>();

   // gaining access to private field
   Field reqField;
   try {
    reqField = PriorityBlockingQueue.class.getDeclaredField("lock");
    reqField.setAccessible(true);
    this.lock = (ReentrantLock)reqField.get(ReentrantLock.class);
    this.notFull = this.lock.newCondition();

   } catch (SecurityException ex) {
    ex.printStackTrace();
    throw ex;
   } catch (NoSuchFieldException ex) {
    ex.printStackTrace();
    throw ex;
   } catch (IllegalAccessException ex) {
    ex.printStackTrace();
    throw ex;
   }

...

@Override
public boolean offer(E e) {
    this.lock.lock();
    try {
        while (this.size() == this.capacity)
            notFull.await();
        boolean success = this.queue.offer(e);
        return success;
    } catch (InterruptedException ie) {
        notFull.signal(); // propagate to a non-interrupted thread
        return false;

    } finally {
        this.lock.unlock();
    }
}
...

这也有一些检测,所以我可以检查队列的有效性。 我还在制作'PivotPriorityBlockingQueue',如果有人有兴趣我可以发布。

答案 4 :(得分:0)

如果要执行的Runnables的顺序不严格(原样:即使存在更高优先级的任务,也可能会执行某些优先级较低的任务),那么我会建议以下内容,这可以归结为定期将PriorityQueue缩小尺寸:

if (queue.size() > MIN_RETAIN * 2){
    ArrayList<T> toRetain = new ArrayList<T>(MIN_RETAIN);
    queue.drainTo(toRetain, MIN_RETAIN);
    queue.clear();
    for (T t : toRetain){
      queue.offer(t);
    }
}

如果订单需要严格,这显然会失败,因为耗尽将导致片刻,wenn低优先级任务将使用并发访问从队列中检索。

优点是,这是线程安全的,并且可能与优先级队列设计一样快。

答案 5 :(得分:0)

到目前为止,没有一个答案具有以下所有属性:

  • 实现BlockingQueue接口。
  • 支持删除绝对最大值。
  • 没有竞争条件。

不幸的是,标准Java库中没有BlockingQueue实现。您将需要找到一个实现或自己实现一些东西。实现BlockingQueue将需要一些正确锁定的知识。

以下是我的建议:查看https://gist.github.com/JensRantil/30f812dd237039257a3d并将其用作模板,以围绕SortedSet实现自己的包装。基本上,所有锁定都在那里,并且有多个单元测试(需要一些调整)。

答案 6 :(得分:0)

the ConcurrencyUtils repo中有一项实施。

答案 7 :(得分:-1)

查看Google Collections API中的ForwardingQueue。对于阻止语义,您可以使用Semaphore

答案 8 :(得分:-1)

还有另一个实现here

似乎做了你要求的事情:

BoundedPriorityQueue实现了一个优先级队列,其中包含元素数量的上限。如果队列未满,则始终添加添加的元素。如果队列已满并且添加的元素大于队列中的最小元素,则删除最小元素并添加新元素。如果队列已满并且添加的元素不大于队列中的最小元素,则不会添加新元素。