java BlockingQueue没有阻塞偷看?

时间:2009-11-18 23:11:42

标签: java queue peek blockingqueue

我有一个对象的阻塞队列。

我想写一个阻塞的线程,直到队列中有一个对象。与BlockingQueue.take()提供的功能类似。

但是,由于我不知道我是否能够成功处理该对象,我想只是peek()而不是删除该对象。我只想在能够成功处理它的情况下删除该对象。

所以,我想要一个阻塞的peek()函数。目前,如果队列为空,则peek()只返回javadocs。

我错过了什么吗?还有其他方法可以实现此功能吗?

修改

如果我只是使用线程安全队列并且偷看和睡觉了?

public void run() {
    while (!exit) {
        while (queue.size() != 0) {
            Object o =  queue.peek();
            if (o != null) {
                if (consume(o) == true) {
                    queue.remove();
                } else {
                    Thread.sleep(10000); //need to backoff (60s) and try again
                }
            }
        }
        Thread.sleep(1000); //wait 1s for object on queue
    }
}

请注意,我只有一个消费者线程和一个(单独的)生产者线程。我想这不如使用BlockingQueue有效......任何评论都赞赏。

8 个答案:

答案 0 :(得分:14)

您可以使用LinkedBlockingDeque并从队列中物理删除该项目(使用takeLast()),但在队列末尾时再次替换它,如果使用{处理失败{1}}。与此同时,您的“制作人”会使用putLast(E e)向队列的前面添加元素。

您始终可以在自己的putFirst(E e)实现中封装此行为,并提供Queue方法,该方法在底层{{1}的幕后执行blockingPeek()后跟takeLast() }}。因此,从调用客户端的角度来看,元素永远不会从队列中删除。

答案 1 :(得分:6)

  

但是,由于我不知道我是否能够成功处理该对象,我想只是peek()而不是删除该对象。我只想在能够成功处理它的情况下删除该对象。

通常,它不是线程安全的。如果在peek()之后确定对象可以成功处理,但在take()删除和处理它之前,另一个线程会获取该对象,该怎么办?

答案 2 :(得分:2)

我唯一知道的是BlockingBuffer中的Apache Commons Collections

  

如果调用get或remove   一个空的Buffer,即调用线程   等待添加或通知   addAll操作已经完成。

get()相当于peek()Buffer可以通过使用BlockingQueue装饰UnboundedFifoBuffer来使其像BlockingBuffer一样行事

答案 3 :(得分:1)

您是否还可以将事件侦听器队列添加到阻塞队列中,然后在(阻塞)队列中添加某些内容时,将事件发送给侦听器?你可以让你的线程阻塞,直到调用了actionPerformed方法。

答案 4 :(得分:1)

快速回答是,并非真的有一种方法可以阻止窥视,禁止用自己的阻塞peek()实现阻塞队列。

  

我错过了什么吗?

peek()在并发方面会很麻烦 -

  • 如果你不能处理你的peek()'消息 - 它将被留在队列中,除非你有多个消费者。
  • 如果您无法处理该对象,谁会将该对象从队列中删除?
  • 如果您有多个消费者,则在您偷看()和另一个处理项目的线程之间会遇到竞争条件,从而导致重复处理或更糟糕。

听起来你可能会更好地删除项目并使用a处理它 Chain-of-responsibility pattern

编辑:re:你的最后一个例子:如果你只有一个消费者,你将永远不会删除队列中的对象 - 除非它在同一时间更新 - 在这种情况下你最好非常小心线程安全,可能不应该把项目放在队列中。

答案 5 :(得分:0)

看起来BlockingQueue本身没有您指定的功能。

我可能会尝试稍微重新解决这个问题:对于无法“正确处理”的对象,你会做什么?如果你只是将它们留在队列中,你必须在某个时候将它们拉出来处理它们。我建议要么弄清楚如何处理它们(通常,如果一个queue.get()给出任何类型的无效或坏的值,你可能只是把它放在地板上)或选择不同的数据结构。一个先进先出。

答案 6 :(得分:0)

本身不是答案,但是:enter image description here声称这不是有效的用例。

答案 7 :(得分:0)

“最简单”的解决方案

  

在成功处理上一个元素之前,请勿处理 next 元素。

public void run() {

Object lastSuccessfullyProcessedElement = null;

    while (!exit) {
        Object obj =  lastSuccessfullyProcessedElement == null ? queue.take() : lastSuccessfullyProcessedElement; // blocking

        boolean successful = process(obj);

        if(!successful) {
            lastSuccessfullyProcessedElement = obj;
        } else {
            lastSuccessfullyProcessedElement = null;
        }
    }
}
  1. 调用peek()并检查该值是否为null不能有效利用CPU。

当以下程序的队列为空时,我发现系统上的CPU使用率达到10%。

while (true) {
   Object o = queue.peek();
   if(o == null) continue;
   // omitted for the sake of brevity
}
  1. 添加sleep()会增加速度。

  2. 使用putLast将其添加回队列将干扰顺序。而且,这是一个需要锁的阻塞操作。