通过使用 notify 代替 notifyAll 来减少线程竞争

Reduce thread competition by using notify in place of notifyAll

我看到这个自己实现的有界blocking queue
对其进行了更改,旨在通过将 notifyAll 替换为 notify.

来消除竞争

但我不太明白添加的 2 个额外变量的意义所在:waitOfferCountwaitPollCount
它们的初始值都是0.

添加前后的差异如下:
Offer:

Poll:

我的理解是,2 个变量的目的是当对象上没有 wait 时,您不会进行无用的 notify 调用。但是如果不这样做会有什么危害呢?
另一个想法是它们可能与从 notifyAllnotify 的转换有关,但我再次认为即使没有它们我们也可以安全地使用 notify

完整代码如下:

class FairnessBoundedBlockingQueue implements Queue {
    protected final int capacity;

    protected Node head;

    protected Node tail;

    // guard: canPollCount, head
    protected final Object pollLock = new Object();
    protected int canPollCount;
    protected int waitPollCount;

    // guard: canOfferCount, tail
    protected final Object offerLock = new Object();
    protected int canOfferCount;
    protected int waitOfferCount;

    public FairnessBoundedBlockingQueue(int capacity) {
        this.capacity = capacity;
        this.canPollCount = 0;
        this.canOfferCount = capacity;
        this.waitPollCount = 0;
        this.waitOfferCount = 0;
        this.head = new Node(null);
        this.tail = head;
    }

    public boolean offer(Object obj) throws InterruptedException {
        synchronized (offerLock) {
            while (canOfferCount <= 0) {
                waitOfferCount++;
                offerLock.wait();
                waitOfferCount--;
            }
            Node node = new Node(obj);
            tail.next = node;
            tail = node;
            canOfferCount--;
        }
        synchronized (pollLock) {
            ++canPollCount;
            if (waitPollCount > 0) {
                pollLock.notify();
            }
        }
        return true;
    }

    public Object poll() throws InterruptedException {
        Object result;
        synchronized (pollLock) {
            while (canPollCount <= 0) {
                waitPollCount++;
                pollLock.wait();
                waitPollCount--;
            }

            result = head.next.value;
            head.next.value = null;
            head = head.next;
            canPollCount--;
        }
        synchronized (offerLock) {
            canOfferCount++;
            if (waitOfferCount > 0) {
                offerLock.notify();
            }
        }
        return result;
    }
}

您需要询问该更改的作者他们认为他们通过该更改实现了什么。

我的看法如下:

  • notifyAll() 更改为 notify() 是一件好事。如果有 N 个线程在等待队列的 offerLockpollLock,那么这可以避免 N - 1 不必要的唤醒。

  • 似乎正在使用计数器避免在没有线程等待时调用 notify()。在我看来,这像是一个值得怀疑的优化。 AFAIK notify 在没有等待的情况下在互斥体上是非常便宜的。所以这可能会产生很小的差异......但不太可能显着。

  • 如果你真的想知道,写一些基准测试。写这个 class 的 4 个版本,没有优化,通知优化,计数器优化和两者。然后比较结果...针对不同级别的队列争用。


我不确定这里的“公平”是什么意思,但是我在这个 class 中看不到任何东西来保证在 offerpoll 得到公平对待。

Another thought is that they may have something to do with the switch from notifyAll to notify, but again I think we can safely use notify even without them?

是的,由于使用了两个锁(pollLock offerLock),所以不用这两个变量,把notyfiAll改成notify也没问题。但是如果你使用的是锁,则必须使用notifyAll.

My understanding is that the 2 variables purpose is that you won't do useless notify calls when there's nothing wait on the object. But what harm would it do if not done this way?

是的,这两个变量是为了避免无用的notify调用。这两个变量还带来了额外的操作。我认为可能需要进行基准测试来确定不同场景下的性能。

此外,

1.As 一个阻塞队列,它应该实现接口 BlockingQueuepolloffer 方法都应该是 non-blocking。它应该使用 takeput.

2.This 不是 Fairness 队列。