通过使用 notify 代替 notifyAll 来减少线程竞争
Reduce thread competition by using notify in place of notifyAll
我看到这个自己实现的有界blocking queue
。
对其进行了更改,旨在通过将 notifyAll
替换为 notify
.
来消除竞争
但我不太明白添加的 2 个额外变量的意义所在:waitOfferCount
和 waitPollCount
。
它们的初始值都是0.
添加前后的差异如下:
Offer
:
Poll
:
我的理解是,2 个变量的目的是当对象上没有 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()
是一件好事。如果有 N
个线程在等待队列的 offerLock
或 pollLock
,那么这可以避免 N - 1
不必要的唤醒。
似乎正在使用计数器避免在没有线程等待时调用 notify()
。在我看来,这像是一个值得怀疑的优化。 AFAIK notify
在没有等待的情况下在互斥体上是非常便宜的。所以这可能会产生很小的差异......但不太可能显着。
如果你真的想知道,写一些基准测试。写这个 class 的 4 个版本,没有优化,通知优化,计数器优化和两者。然后比较结果...针对不同级别的队列争用。
我不确定这里的“公平”是什么意思,但是我在这个 class 中看不到任何东西来保证在 offer
或 poll
得到公平对待。
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 一个阻塞队列,它应该实现接口 BlockingQueue
,poll
和 offer
方法都应该是 non-blocking
。它应该使用 take
和 put
.
2.This 不是 Fairness
队列。
我看到这个自己实现的有界blocking queue
。
对其进行了更改,旨在通过将 notifyAll
替换为 notify
.
但我不太明白添加的 2 个额外变量的意义所在:waitOfferCount
和 waitPollCount
。
它们的初始值都是0.
添加前后的差异如下:
Offer
:
Poll
:
我的理解是,2 个变量的目的是当对象上没有 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()
是一件好事。如果有N
个线程在等待队列的offerLock
或pollLock
,那么这可以避免N - 1
不必要的唤醒。似乎正在使用计数器避免在没有线程等待时调用
notify()
。在我看来,这像是一个值得怀疑的优化。 AFAIKnotify
在没有等待的情况下在互斥体上是非常便宜的。所以这可能会产生很小的差异......但不太可能显着。如果你真的想知道,写一些基准测试。写这个 class 的 4 个版本,没有优化,通知优化,计数器优化和两者。然后比较结果...针对不同级别的队列争用。
我不确定这里的“公平”是什么意思,但是我在这个 class 中看不到任何东西来保证在 offer
或 poll
得到公平对待。
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 一个阻塞队列,它应该实现接口 BlockingQueue
,poll
和 offer
方法都应该是 non-blocking
。它应该使用 take
和 put
.
2.This 不是 Fairness
队列。