【问题标题】:Reduce thread competition by using notify in place of notifyAll通过使用 notify 代替 notifyAll 减少线程竞争
【发布时间】:2021-12-21 06:42:17
【问题描述】:

我看到了这个自我实现的有界blocking queue
对其进行了更改,旨在通过将 notifyAll 替换为 notify 来消除竞争。

但我不太明白添加的 2 个额外变量有什么意义:waitOfferCountwaitPollCount
它们的初始值都是0。

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

Poll:

我的理解是,这两个变量的目的是当对象上没有 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 只会唤醒众多服务员中的一个服务员。但是,我可能是错的。

标签: java multithreading concurrency blockingqueue


【解决方案1】:

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

我的看法如下:

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

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

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


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

【讨论】:

    【解决方案2】:

    另一个想法是它们可能与从 notifyAll 到 notify 的切换有关,但我再次认为即使没有它们我们也可以安全地使用 notify?

    是的,由于使用了两个锁(pollLock offerLock),所以在没有这两个变量的情况下将notyfiAll 更改为notify 是没有问题的。但如果你使用锁,你必须使用notifyAll

    我的理解是,这 2 个变量的目的是当对象没有等待时,您不会进行无用的通知调用。但是如果不这样做会有什么坏处呢?

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

    另外,

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

    2.这不是Fairness 队列。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-10
      • 1970-01-01
      相关资源
      最近更新 更多