DelayQueue 中的 leader 到底是做什么用的呢?

What exactly is the leader used for in DelayQueue?

我试图理解 java.util.concurrent 中的 DelayQueue,但 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();
    }
}

其次,似乎没有减少不必要的定时等待。根据注释:

This variant of the Leader-Follower pattern (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to minimize unnecessary timed waiting

我认为这是最小化 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

那么,有人可以向我解释一下吗?我在某处有错误的想法吗?

根据源码注释:

It waits only for the next delay to elapse, but other threads await indefinitely.

领导者不用于minimizing awaitNanos,它用于避免不必要的唤醒和睡眠。如果让所有线程available.awaitNanos(delay)都在take方法中,它们会同时被调用,但只有一个线程能真正从队列中获取元素,其他线程会再次陷入休眠,这是不必要的和资源浪费.

在 Leader-Follower 模式下,领导线程 available.awaitNanos(delay),非领导线程 available.await()。因此领导者将首先醒来并检索一个过期的元素,然后在必要时向另一个等待线程发出信号。这样效率更高。

例子

假设我在队列中有一个元素E,它会在T纳秒后失效,并且有两个线程Thread_1Thread_2

没有leader(你在问题中提供的实现)

  • Thread_1调用take方法,T纳秒后发现E可用,于是调用available.awaitNanos(T).
  • Thread_2调用take方法,T纳秒后发现E可用,于是调用available.awaitNanos(T).

T 纳秒后,Thread_1 醒来并获取元素 EThread_2一觉醒来一无所获,只好又睡了。 不需要Thread_2的唤醒和睡眠。

有领导

  • Thread_1调用take方法,T纳秒后发现E可用,成为leader,调用available.awaitNanos(T).
  • Thread_2调用take方法,发现E在T纳秒后可用,但是Thread_2也注意到已经有leader,所以Thread_2调用available.await().

T 纳秒后,Thread_1 醒来并获取元素 EThread_2 将休眠直到有新元素放入队列。

我想回答 McGar 的最后一条评论,但没有这样做的声誉。

所以我认为领导线程不能保证首先被调用。这里的技巧是当你提供一个新的最早的项目时,你手动设置 leader = null,然后被唤醒的线程将成为新的领导者,无论它是否曾经是领导者。