在 java 中实现你自己的阻塞队列

我知道这个问题以前已经被问过很多次和回答过,但我就是想不出在互联网上找到的例子的技巧,比如这个那个

这两种解决方案都会检查阻塞队列的数组/队列/链接列表在方法中是否空到等待线程,反之亦然。第二个链接中的注释强调了这种情况,并提到这是不必要的。notifyAllput()get()

所以问题是;对我来说,检查队列是否为空似乎有点奇怪|full 以通知所有等待的线程。有什么想法吗?

提前致谢。


答案 1

我知道这已经是一个老问题了,但是在阅读了问题和答案之后,我无法帮助自己,我希望你觉得这很有用。

关于在通知其他等待线程之前检查队列实际上是满的还是空的,你错过了一些东西,这两个方法都是方法,这意味着一次只有一个线程可以进入这些方法之一,但这不会阻止它们一起工作,所以如果一个线程a已经进入了方法,另一个线程b仍然可以在线程a退出之前进入并开始执行方法中的指令。 所以这个设计会让开发人员感觉更安全一点,因为你无法知道未来的cpu上下文切换是否会发生,或者什么时候会发生。put (T t)T get()synchronizedput (T t)T get()put (T t)double-checking

一种更好和更推荐的方法是使用和:Reentrant LocksConditions

我已经编辑了此链接的源代码

Condition isFullCondition;
Condition isEmptyCondition;
Lock lock;

public BQueue() {
    this(Integer.MAX_VALUE);
}

public BQueue(int limit) {
    this.limit = limit;
    lock = new ReentrantLock();
    isFullCondition = lock.newCondition();
    isEmptyCondition = lock.newCondition();
}

public void put (T t) {
    lock.lock();
    try {
       while (isFull()) {
            try {
                isFullCondition.await();
            } catch (InterruptedException ex) {}
        }
        q.add(t);
        isEmptyCondition.signalAll();
    } finally {
        lock.unlock();
    }
 }

public T get() {
    T t = null;
    lock.lock();
    try {
        while (isEmpty()) {
            try {
                isEmptyCondition.await();
            } catch (InterruptedException ex) {}
        }
        t = q.poll();
        isFullCondition.signalAll();
    } finally { 
        lock.unlock();
    }
    return t;
}

使用这种方法不需要 ,因为对象在两个方法之间共享,这意味着一次只有一个线程 a 或 b 可以输入这些方法中的任何一个,这与创建不同监视器的同步方法不同,并且只有那些因为队列已满而等待的线程才会在有更多空间时收到通知, 对于等待的线程也是如此,因为队列是空的,这将导致更好的CPU利用率。你可以在这里找到更详细的源代码示例double checkinglock


答案 2

我认为从逻辑上讲,在之前进行额外的检查没有坏处。notifyAll()

你可以简单地一旦你把/从队列中得到一些东西。一切仍然可以工作,并且您的代码更短。但是,在调用 之前,检查是否有人可能正在等待(通过检查是否达到队列边界)也没有坏处。这个额外的逻辑片段节省了不必要的调用。notifyAll()notifyAll()notifyAll()

这只取决于您想要更短,更干净的代码,或者您希望代码更有效地运行。(尚未研究 的实现。如果这是一个廉价的操作,如果没有人等待,那么对于额外的检查来说,性能提升可能并不明显)notifyAll()