Java-并发性:LinkedBlockingQueue;“如果失败,请重试”;

Java-并发性:LinkedBlockingQueue;“如果失败,请重试”;,java,multithreading,concurrency,queue,Java,Multithreading,Concurrency,Queue,但我有点担心在多线程环境中旋转实现 我做不到 while(!q.offer(e)); 或者是因为唤醒呼叫将转到条件的内部(私有)实例,这意味着这将是安眠药自杀 不过,我也不是特别喜欢 synchronized(q){ while(!q.offer(e))q.wait(); } (尽管这在网上的例子中似乎是一个流行的选择),因为尽管这会让我等得更久,但我知道没有可靠的方法来检测异常何时会迫使我再试一次。 我可以做类似的事情 try{ q.put(e); }catch(Inter

但我有点担心在多线程环境中旋转实现

我做不到

while(!q.offer(e));
或者是因为唤醒呼叫将转到
条件的内部(私有)实例,这意味着这将是安眠药自杀

不过,我也不是特别喜欢

synchronized(q){
    while(!q.offer(e))q.wait();
}
(尽管这在网上的例子中似乎是一个流行的选择),因为尽管这会让我等得更久,但我知道没有可靠的方法来检测异常何时会迫使我再试一次。 我可以做类似的事情

try{
    q.put(e);
}catch(InterruptedException ex){}
但是,如果在
put
success
赋值之间发生异常,那么我会多次将同一元素排队

我可以

boolean success = false;
do{
    try{
        q.put(e);
        success = true;
    }catch(InterruptedException ex){}
}while(!success)
但我记得(很久以前)读过,你不应该依赖条件的异常处理(尽管我似乎不记得为什么不鼓励这样做)

所以。。。我有什么选择?我需要旋转还是有更智能的东西?

所以下面几点:

  • put
    方法仅在两种情况下抛出异常:
    • 线程被中断,在这种情况下,您应该停止正在执行的任何操作。有关
      InterruptedException
      s的更多信息,请参阅
    • 您插入的元素是
      null
      ,这完全是另一个bug
因此,总体而言,您可以使用
put
等待空间变为可用。如果抛出了这些特定异常中的任何一个,则无论如何都不应重试。

执行
put()
将是正确的,直到中断或成功为止。如果您所做的只是旋转,那么
offer()
是一个坏主意(请参阅第一条注释以获取免责声明)

正如Nicolas解释的那样,
InterruptedException
的处理并不简单,在很大程度上取决于您的其他代码在做什么,但您担心的是“如果异常发生在put和分配成功之间”,这是永远不会发生的:阻塞调用(如
put()
)可以引发该异常,但是它不能发生在
put()
和赋值之间,也不能发生在赋值处


最后,不需要对任何内容进行同步。许多
java.util.concurrent
类的主要思想是避免或抽象掉显式同步。

像您这样捕获
中断异常是不好的做法,因为您的代码不再响应中断。
InterruptedException
通常由响应中断(当前
线程被中断)的方法引发,例如
wait
wait
join
sleep
等类型的方法,这不应被视为失败,而应视为真正的失败,需要考虑的
线程的状态更改

正如Brian Goetz在
Java并发实践
中所解释的,我引用:

当您的代码调用抛出
InterruptedException
的方法时 您的方法也是一种阻塞方法,必须为 对中断作出反应。对于库代码,基本上有两种 选择:

传播InterruptedException。这通常是最明智的策略,如果您可以不受影响,只需传播
InterruptedException
发送给您的调用者。这可能包括不抓人
InterruptedException
,或者捕获它并在之后再次抛出它 执行一些简短的特定于活动的清理

恢复中断。有时您不能抛出
中断异常
,例如,当您的代码是
Runnable
。在这些情况下,您必须捕获
InterruptedException
并通过在上调用
interrupt
来恢复中断状态 当前线程,以便调用堆栈更高级别的代码可以看到 中断被发出

因此,在您的情况下,您应该简单地使用
put(E)
,因为它将使调用线程在需要时等待空间变为可用,并传播
InterruptedException
,以保持对中断的响应


但是如果 异常发生在
put
和分配给
成功


这可以简单地从不发生,因为
布尔值的赋值将从不抛出任何异常(非装箱情况下的NPE除外)。只有响应中断的方法才能抛出上述类型的异常,这显然不是赋值的情况。

您认为按名称阻塞队列意味着什么?为什么需要通过它进行同步?@SMA,因为该代码段中的要点是让线程等待,而
offer
没有。通常应该忽略一个虚假的中断,而不是停止一切。在所有情况下,在
offer
上旋转都不是一个坏主意。如果您正在寻找一个高吞吐量系统,那么短暂地旋转可能比通过阻塞放弃对处理器的控制更痛苦。但这只是一个小问题,只适用于极少数的用例。@JoeC确实是一个更罕见的用例。虚假中断?“你不是把这和虚假的唤醒混淆了吗?”鲍莫尔说得好。不应该在累的时候这样做。
boolean success = false;
do{
    try{
        q.put(e);
        success = true;
    }catch(InterruptedException ex){}
}while(!success)
boolean success = true;
do{
    try{
        q.put(e);
    }catch(InterruptedException ex){
        success = false;
    }
}while(!success)