我试图了解DelayQueue
中的java.util.concurrent
,但leader
让我困惑。
首先,我们可以实现一个没有leader
的DelayQueue,如下所示:
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e);
if (q.peek() == e) {
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null) {
available.await();
} else {
long delay = first.getDelay(TimeUnit.NANOSECONDS);
if (delay <= 0) {
return q.poll();
} else {
available.awaitNanos(delay);
}
}
}
} finally {
lock.unlock();
}
}
其次,似乎最小化不必要的定时等待。根据注释:
Leader-Follower模式的这种变体(http://www.cs.wustl.edu/~schmidt/POSA/POSA2/)用于最小化不必要的定时等待
我将其视为最小化awaitNanos
(使用await
而不是awaitNanos
),但我真的对此表示怀疑。如果新元素不是队列的头部,则不会发出任何线程信号。 (参见下面的offer
方法)
if (q.peek() == e) {
leader = null; // set leader to null
available.signal();
}
因此,当新元素是头部时,它才会有所不同。但在这种情况下,leader
将设置为null
,并且发出信号的线程不会采用这种方式(take
方法):
else if (leader != null)
available.await();
线程将始终awaitNanos
。
那么,有人可以向我解释一下吗?我在某处弄错了吗?
答案 0 :(得分:1)
根据源代码的评论:
它只等待下一个延迟过去,但其他线程等待 下去。
领导者不用于minimizing awaitNanos
,它用于避免不必要的唤醒&amp;睡觉。如果你在available.awaitNanos(delay)
方法中允许所有线程take
,它们将同时被调用,但只有一个可以从队列中获得元素,其他线程将再次进入休眠状态,这是不必要的和资源浪费
使用Leader-Follower模式,领导者available.awaitNanos(delay)
,非领导者帖子available.await()
。因此,领导者将首先唤醒并检索已释放的元素,然后在必要时发出另一个等待线程的信号。这样效率更高。
假设队列中有一个元素E
,它将在T
纳秒后显示,并且有两个线程Thread_1
和Thread_2
。
没有领导者(您在问题中提供的实施方案)
Thread_1
调用take
方法,发现E在T纳秒后可用,因此调用available.awaitNanos(T)
。Thread_2
调用take
方法,发现E在T纳秒后可用,因此调用available.awaitNanos(T)
。在T
纳秒后,Thread_1
唤醒并获取元素E
。 Thread_2
醒来,什么也得不到,所以它必须再次入睡。 Thread_2
的唤醒和再次睡眠是不必要的。
有领导
Thread_1
调用take
方法,发现E在T纳秒后可用,因此它成为领导者并调用available.awaitNanos(T)
。Thread_2
调用take
方法,发现E在T纳秒后可用,但Thread_2
也注意到已有领导者,因此Thread_2
调用available.await()
}。在T
纳秒后,Thread_1
唤醒并获取元素E
。 Thread_2
将一直睡眠,直到新元素被放入队列中。
答案 1 :(得分:0)
我想回答 McGar 的最后一条评论,但没有声誉这样做。
所以我认为不能保证首先调用领导线程。这里的技巧是当你提供一个新的最早的item时,你手动设置leader = null,然后被唤醒的线程将成为新的leader,不管它以前是不是leader。