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)