在java中实现您自己的阻塞队列
我知道这个问题以前被问过很多次,也被回答过很多次,但我就是无法从互联网上的例子中找出一个窍门,比如一个 这两种解决方案都在在java中实现您自己的阻塞队列,java,multithreading,synchronization,java.util.concurrent,blockingqueue,Java,Multithreading,Synchronization,Java.util.concurrent,Blockingqueue,我知道这个问题以前被问过很多次,也被回答过很多次,但我就是无法从互联网上的例子中找出一个窍门,比如一个 这两种解决方案都在put()方法中检查阻塞队列的数组/队列/链接列表是否为空,以notifyAll等待线程,在get()方法中反之亦然。第二个链接中的A强调了这种情况,并提到没有必要这样做 所以问题是,;对我来说,检查队列是否为空,通知所有等待的线程也有点奇怪。有什么想法吗 提前谢谢。我认为在notifyAll()之前进行额外检查在逻辑上没有坏处 一旦您从队列中放入/获取某些内容,您就可以简单
put()
方法中检查阻塞队列的数组/队列/链接列表是否为空,以notifyAll
等待线程,在get()
方法中反之亦然。第二个链接中的A强调了这种情况,并提到没有必要这样做
所以问题是,;对我来说,检查队列是否为空,通知所有等待的线程也有点奇怪。有什么想法吗
提前谢谢。我认为在
notifyAll()之前进行额外检查在逻辑上没有坏处
一旦您从队列中放入/获取某些内容,您就可以简单地notifyAll()
。一切都将继续工作,并且您的代码更短。但是,在调用notifyAll()
之前检查是否有人可能在等待(通过检查是否到达队列边界)也没有坏处。这一额外的逻辑节省了不必要的notifyAll()
调用
这取决于您希望代码更短、更干净,或者希望代码运行更高效。(没有研究过notifyAll()
的实现。如果没有人等待,那么这是一个廉价的操作,那么额外检查的性能增益可能并不明显)作者使用notifyAll()
的原因很简单:他们不知道这是否必要,所以他们决定“更安全”选择权
在上面的示例中,只需调用notify()
就足够了。对于添加的每个元素,在所有情况下都只能提供一个线程等待
如果您的队列也可以选择在一个步骤中添加多个元素,如addAll(Collection list)
,那么这一点就变得更加明显了,因为在这种情况下,可以为多个等待空列表的线程提供服务,确切地说:添加的线程数与添加的元素数相同
然而,在特殊的单元素情况下,notifyAll()
会导致额外的开销,因为许多线程被不必要地唤醒,因此必须再次进入睡眠状态,同时阻塞队列访问。因此,在这种特殊情况下,将notifyAll()
替换为notify()
将提高速度
但是根本不使用wait/notify和synchronized,而是使用并发包将比任何智能wait/notify实现都能提高速度。我知道这是一个老问题,但在阅读了问题和答案后,我忍不住要问自己,我希望你觉得这有用 关于在通知其他等待的线程之前检查队列是否已满或为空,您缺少了一些方法,这两种方法都是
put(T)
和T get(),但这不会阻止它们一起工作,因此如果一个线程a已经输入了put(T)
方法,另一个线程b仍然可以在线程a退出put(T)
之前输入并开始执行T get()
方法中的指令,因此,这种双重检查
设计将使开发人员感到更安全一点,因为您不知道将来是否会或何时会发生cpu上下文切换
更好、更推荐的方法是使用可重入锁
和条件
:
//我已经编辑了这篇文章的源代码
使用这种方法不需要重复检查,因为两个方法之间共享了锁定对象,这意味着一次只能有一个线程a或b进入这些方法中的任何一个,这与创建不同监视器的同步方法不同,只有那些因为队列已满而等待的线程在有更多空间时才会收到通知,而等待的线程也是如此,因为队列为空,这将提高cpu利用率。
您可以通过源代码找到更详细的示例我想编写一个简单的阻塞队列实现,它将帮助人们轻松理解这一点。这是为新手准备的
class BlockingQueue {
private List queue = new LinkedList();
private int limit = 10;
public BlockingQueue(int limit){
this.limit = limit;
}
public synchronized void enqueue(Object ele) throws InterruptedException {
while(queue.size() == limit)
wait();
if(queue.size() == 0)
notifyAll();
// add
queue.add(ele);
}
public synchronized Object deque() throws InterruptedException {
while (queue.size() == 0)
wait();
if(queue.size() == limit)
notifyAll();
return queue.remove(0);
}
}
部分问题涉及在并发程序中正确使用等待
和通知
的困难;引用Joshua Bloch的话,它们就像“并发编程的低级(汇编语言)”。他提倡使用notifyAll
,等待线程在收到通知时应始终在循环中进行检查,以便它们可以根据需要继续等待。实际上,您根本不应该使用wait/notify,而是始终计划使用JavaSE5中提供的更高级别并发API。不是线程和等待/通知,而是从任务和执行者的角度设计并发应用。是的,我完全同意你的观点,老一套但好一点:没有必要重新发明轮子。但是想想看,我们需要在这个案例中使用wait和notifyAll。我知道wait()应该在循环中使用,所有这些都在javadocs(虚假唤醒)中解释过。但是黑客们对通知的条件有什么看法呢?这是我无法理解的要点。请看-他们使用了ReentrantLock
和条件
s,而不是等待
/通知
。我们正在脱离问题的范围!我确实在寻找实际的实现,但对于自实现的阻塞队列来说,这可能是最简单的方法,为什么在不同的解决方案中存在相同的“如果”条件?只是复制粘贴?无意识编码?:)我正在阅读jenkov的教程,据我所知,在deque
中添加了if(this.queue.size()==this.limit){notifyAll();}
,因为
class BlockingQueue {
private List queue = new LinkedList();
private int limit = 10;
public BlockingQueue(int limit){
this.limit = limit;
}
public synchronized void enqueue(Object ele) throws InterruptedException {
while(queue.size() == limit)
wait();
if(queue.size() == 0)
notifyAll();
// add
queue.add(ele);
}
public synchronized Object deque() throws InterruptedException {
while (queue.size() == 0)
wait();
if(queue.size() == limit)
notifyAll();
return queue.remove(0);
}