Java - 并发:LinkedBlockingQueue,“如果失败则再试一次”

时间:2016-10-02 15:31:10

标签: java multithreading concurrency queue

int capacity = ...
BlockingQueue q = new LinkedBlockingQueue<Element>(capacity);

现在,我确实感到有点荒谬的问这个,但是当谈到java并发时我并不是特别精明,所以我会很感激选择正确排队方式的一些帮助(并且实际上会出列,但我希望我们清理了一个,另一个将自行落实到位。)

当然有

while(!q.offer(e));

但我对在多线程环境中旋转实现有点警惕。

我做不到

synchronized(q){
    while(!q.offer(e))q.wait();
}

或者是因为唤醒调用会转到Condition的内部(私有)实例,这意味着这将是一个自杀式的避孕药实现。

但是,我也不是特别喜欢

try{
    q.put(e);
}catch(InterruptedException ex){}

(即使它确实似乎是在线示例中的热门选择)因为虽然这会等待我,但我知道没有可靠的方法来检测异常何时会迫使我再试一次。 我可以做类似

的事情
boolean success = false;
do{
    try{
        q.put(e);
        success = true;
    }catch(InterruptedException ex){}
}while(!success)

但是如果异常发生在putsuccess的分配之间,那么我最终会多次将相同的元素排入队列。

我能做到

boolean success = true;
do{
    try{
        q.put(e);
    }catch(InterruptedException ex){
        success = false;
    }
}while(!success)

但是我记得曾经读过(回头)你不应该依赖条件的异常处理(尽管我似乎不记得为什么不鼓励这样做。)

那么......我有什么选择?我需要旋转还是有更聪明的东西?

3 个答案:

答案 0 :(得分:1)

put()实施将是正确的,阻止直到中断或成功。 offer()是一个坏主意,如果您正在做的就是旋转(请参阅免责声明的第一条评论)。

正如尼古拉斯解释的那样,对InterruptedException的处理并不简单,并且很大程度上取决于你的其他代码在做什么,而是你对&#34;如果异常发生在put和成功的分配&#34;,永远不会发生:阻塞调用(如put())可以抛出该异常,但不能在put()与赋值之间或在赋值时发生。

最后,没有必要同步任何事情。许多java.util.concurrent类的主要思想是避免或抽象出显式同步。

答案 1 :(得分:1)

抓住InterruptedException并不是一个好习惯,因为您的代码将不再响应中断。 InterruptedException通常由响应中断(当前Thread被中断)的方法引发,例如awaitwaitjoin类型的方法, sleep和其他许多人一样,这不应该被视为失败,而是实际上,Thread的状态变化需要加以考虑。

正如 Brian Goetz Java Concurrency in Practice中解释的那样,我引用:

  

当您的代码调用抛出InterruptedException的方法时,那么   你的方法也是一种阻止方法,并且必须有一个计划   应对中断。对于库代码,基本上有两个   选择:

     

传播InterruptedException。这通常是最明智的策略,如果你可以逃脱它只是传播   InterruptedException给你的来电者。这可能涉及不捕捉   InterruptedException,或者抓住它并在之后重新扔掉它   执行一些简短的活动清理。

     

恢复中断。有时您无法抛出InterruptedException,例如当您的代码属于某个代码时   Runnable。在这些情况下,您必须抓住InterruptedException   并通过调用interrupt来恢复中断状态   当前线程,以便调用堆栈上方的代码可以看到一个   发出了中断。

因此,在您的情况下,您应该只使用put(E),因为它会使调用线程在需要时等待空间可用,传播InterruptedException 以便继续对中断做出反应。

  

但是如果是的话,我最终会多次将相同的元素排入队列   异常发生在put和赋值之间   success

这可以简单地从不发生,因为boolean的赋值将从不抛出任何异常(在解除封锁的情况下除了NPE)。并且只有响应中断的方法才会抛出如上所述的这种异常,这显然不是指派的情况。

答案 2 :(得分:0)

来自LinkedBlockingQueue Javadoc的几点:

  • put方法只会在两种情况下抛出异常:
    • 线程被中断,在这种情况下你应该停止你正在做的任何事情。有关InterruptedException s。
    • 的详情,请参阅this question
    • 您要插入的元素是null,这完全是另一个错误。

总的来说,您可以使用put等待空间可用。如果抛出这些特殊异常中的任何一个,那么你无论如何都不应该重试。