通过使用 notify 代替 notifyAll 减少线程竞争
Posted
技术标签:
【中文标题】通过使用 notify 代替 notifyAll 减少线程竞争【英文标题】:Reduce thread competition by using notify in place of notifyAll 【发布时间】:2021-12-21 06:42:17 【问题描述】:我看到了这个自我实现的有界blocking queue
。
对其进行了更改,旨在通过将 notifyAll
替换为 notify
来消除竞争。
但我不太明白添加的 2 个额外变量有什么意义:waitOfferCount
和 waitPollCount
。
它们的初始值都是0。
添加前后的差异如下:Offer
:
Poll
:
我的理解是,这两个变量的目的是当对象上没有 wait
时,您不会进行无用的 notify
调用。但是如果不这样做会有什么危害呢?
另一个想法是它们可能与从notifyAll
到notify
的切换有关,但我认为即使没有它们我们也可以安全地使用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
只会唤醒众多服务员中的一个服务员。但是,我可能是错的。
【参考方案1】:
您需要询问该更改的作者,他们认为通过该更改可以实现什么。
我的看法如下:
从notifyAll()
更改为notify()
是一件好事。如果有N
线程在队列的offerLock
或pollLock
上等待,那么这可以避免N - 1
不必要的唤醒。
似乎正在使用计数器避免在没有线程等待时调用notify()
。这在我看来像是一个值得怀疑的优化。 AFAIK notify
在没有等待的情况下在互斥锁上非常便宜。所以这可能会产生很小的影响......但它不太可能是显着的。
如果您真的想知道,请编写一些基准测试。编写这个类的 4 个版本,没有优化,通知优化,计数器优化和两者。然后比较结果……针对不同级别的队列争用。
我不确定这里的“公平”应该是什么意思,但我在这个类中看不到任何东西来保证在 offer
或 poll
中等待的线程得到公平对待。
【讨论】:
【参考方案2】:另一个想法是它们可能与从 notifyAll 到 notify 的切换有关,但我再次认为即使没有它们我们也可以安全地使用 notify?
是的,由于使用了两个锁(pollLock
和offerLock
),所以在没有这两个变量的情况下将notyfiAll
更改为notify
是没有问题的。但如果你使用锁,你必须使用notifyAll
。
我的理解是,这 2 个变量的目的是当对象没有等待时,您不会进行无用的通知调用。但是如果不这样做会有什么坏处呢?
是的,这两个变量是为了避免无用的notify
调用。这两个变量还带来了额外的操作。我认为可能需要进行基准测试来确定不同场景下的性能。
另外,
1.作为阻塞队列,它应该实现接口BlockingQueue
,poll
和offer
方法都应该是non-blocking
。它应该使用take
和put
。
2.这不是Fairness
队列。
【讨论】:
以上是关于通过使用 notify 代替 notifyAll 减少线程竞争的主要内容,如果未能解决你的问题,请参考以下文章
为啥wait,notify和notifyAll必须在同步块或同步方法中调