首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java阻塞队列

Java阻塞队列
EN

Code Review用户
提问于 2011-12-19 18:43:08
回答 5查看 16.9K关注 0票数 15
代码语言:javascript
复制
public class BQueue<T> {

    private Queue<T> q = new LinkedList<T>();
    private int limit;

    public BQueue(int limit) {
        this.limit = limit;
    }

    public synchronized void put (T t) throws InterruptedException {
        while (isFull()) {
            wait();
        }
        boolean e = isEmpty();
        q.add(t);
        if (e)
            notifyAll();
    }


    public synchronized T get () throws InterruptedException {
        while (isEmpty()) {
            wait();
        }
        boolean f = isFull();
        T t = q.poll();
        if (f)
            notifyAll();
        return t;
    }

    private boolean isEmpty() {
        return q.size() == 0;
    }
    private boolean isFull() {
        return q.size() == limit;
    }
}

这个实现线程安全吗?

EN

回答 5

Code Review用户

发布于 2011-12-19 21:38:19

是的,它是线程安全的。(至少我没有发现任何问题。)请注意,Java框架已经有了一个BlockingQueue接口,它有一些实现,例如LinkedBlockingQueue (来源)。它可能测试得很好,add有更好的性能,所以如果没有必要的话,尽量不要重新发明轮子。

其他一些注意事项:

  1. 试着使用较长的变量名: be f= isFull();它可能是isFull,这样可以产生更易读的代码。qt也是如此。(我会把它重命名为queueitem。)
  2. 检查您的输入:当limit0或小于零时会发生什么?(您应该抛出一个IllegalArgumentException。)
  3. limitq字段可以标记为final。这将提高代码的可读性,因为读取器不需要检查它们的值是否在类中的某个地方发生了变化。它还将防止意外的价值修改。
  4. 使用的Queue有一个isEmpty方法(通过实现Collection.isEmpty()),您可以使用它而不是自己的方法。
票数 8
EN

Code Review用户

发布于 2012-01-26 16:54:51

我觉得这挺安全的。然而,我对驱动通知的逻辑感到困惑。

put()中,等待队列非满(即至少打开一个时隙),然后将元素t添加到队列的末尾。酷,但是在添加t之前,只有当队列为空时才会通知其他线程。类似地,在get()中,您等待队列非空(即至少有一个项),然后从队列的头获取元素t。同样,很酷,但是只有当队列在获取t之前已满时,才会通知其他线程。这样做的效果是要求队列在清空前被完全填满,反之,在重新填充队列之前将其完全清空。

也许这就是你的意图,但这并没有在行为中被记录下来,也不是典型的阻塞队列。

我想你可能想要的是:

代码语言:javascript
复制
public synchronized void put (T t) throws InterruptedException {
    while (isFull())
        wait();
    q.add(t);
    notifyAll();
}

以及:

代码语言:javascript
复制
public synchronized T get () throws InterruptedException {
    while (isEmpty())
        wait();
    T t = q.poll();
    notifyAll();
    return t;
}

此版本的put()将在添加新元素后立即通知等待线程。这将唤醒所有等待的get()调用,并立即获取您刚才添加的值。这通常是你想要的。如果任何其他等待的put()调用醒来后发现队列已满(因为您刚刚在可能等待它为非满之后添加了一个元素),它将简单地在while循环中循环并开始再次等待;没有造成任何伤害。

类似地,此版本的get()将在删除元素后立即通知等待线程。这将立即唤醒所有等待的put()调用,并允许它们存储新值。这通常也是你想要的。如果任何其他等待的get()调用醒来后发现队列是空的(因为您可能刚刚删除了最后一个元素),它将简单地在while循环中循环,然后再次开始等待,直到其他人添加了另一个元素。

除此之外,在我看来还不错!

附注:-由于get()put()是同步的,所以在从队列中实际删除项之前调用notifyAll()没有什么害处,因为其他等待线程在退出get()之前实际上无法再次运行(因为您没有等待)。因此,它可以进一步简化:

代码语言:javascript
复制
public synchronized T get () throws InterruptedException {
    while (isEmpty())
        wait();
    notifyAll();
    return q.poll();
}
票数 3
EN

Code Review用户

发布于 2011-12-19 19:46:07

在我看来它是安全的。但不要相信我的话,你最好把每一个答案都当作一张选票,按照大多数人说的去做。(多线程是很棘手的!)注:评论不应被视为答案。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/7002

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档