领导者在DelayQueue中使用了什么?

时间:2018-01-29 03:08:50

标签: java multithreading concurrency java.util.concurrent blockingqueue

我试图了解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

那么,有人可以向我解释一下吗?我在某处弄错了吗?

2 个答案:

答案 0 :(得分:1)

根据源代码的评论:

  

它只等待下一个延迟过去,但其他线程等待   下去。

领导者不用于minimizing awaitNanos,它用于避免不必要的唤醒&amp;睡觉。如果你在available.awaitNanos(delay)方法中允许所有线程take,它们将同时被调用,但只有一个可以从队列中获得元素,其他线程将再次进入休眠状态,这是不必要的和资源浪费

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

实施例

假设队列中有一个元素E,它将在T纳秒后显示,并且有两个线程Thread_1Thread_2

没有领导者(您在问题中提供的实施方案)

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

T纳秒后,Thread_1唤醒并获取元素EThread_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唤醒并获取元素EThread_2将一直睡眠,直到新元素被放入队列中。

答案 1 :(得分:0)

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

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