非阻塞DelayQueue,Java

时间:2015-02-06 14:18:41

标签: java concurrency delayed-job delayed-execution

我使用Java的DelayQueue在第二次延迟后发送事件。但问题是,在负载很重的情况下,我的消费者在DelayQueue上阻塞,直到来自另一个线程的相当大量的offer()操作都消失了。

有没有人知道Java中的非阻塞延迟队列实现?

2 个答案:

答案 0 :(得分:1)

我认为您误解了DelayQueue API或线程调度的工作方式。

如果您想要真正的非阻塞队列操作,那么DelayQueue已经提供了它们。例如,poll()将立即返回队列条目或null。它不会阻止调用线程。 offer(...)方法是无阻塞插入的等效方法。

另一方面,如果你实际上说某些线程正在“饥饿”,那么你就无法做到这一点。 Java线程调度不“公平”:

  • 如果您有许多可运行的线程,则不会尝试为每个线程提供类似的运行时间。

  • 如果您有多个线程正在等待原始锁定或通知,那么调度程序将不会尝试“公平地”选择一个线程。

如果您拥有比运行核心更多的线程,线程饥饿将更有可能。

最好的解决方案是设计算法,以确定线程是否被不公平地调度。没关系;见Is a DelayQueue without fairness problematic?


为了记录,我不知道公布日程安排的DelayQueue替换。

答案 1 :(得分:0)

不幸的是,DelayQueue阻塞了队列,如果它被强烈写入,它就不会立即返回,因为它使用了锁。

public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        E first = q.peek();
        if (first == null || first.getDelay(NANOSECONDS) > 0)
            return null;
        else
            return q.poll();
    } finally {
        lock.unlock();
    }
}

因此,如果许多线程写信给它,正如斯蒂芬所说的那样,你无能为力。

我通过将ConcurrentSkipListSet与DelayedElement结合使用来解决问题。

public class DelayedElement implements Comparable<DelayedElement> {

private final Long initTime;
private final String msgId;

public DelayedElement(Long initTime, String msgId) {
    this.initTime = initTime;
    this.msgId = msgId;
}             

@Override
public int hashCode() {
    int hash = 5;
    hash = 29 * hash + Objects.hashCode(this.initTime);
    hash = 29 * hash + Objects.hashCode(this.msgId);
    return hash;
}

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final DelayedElement other = (DelayedElement) obj;
    if (!Objects.equals(this.initTime, other.initTime)) {
        return false;
    }
    if (!Objects.equals(this.msgId, other.msgId)) {
        return false;
    }
    return true;
}
@Override
public int compareTo(DelayedElement o) {
    return -o.initTime.compareTo(initTime);
    }
}

在我的制片人中&#39;线程,我添加了每个元素的延迟。 在我的消费者主题中,我只是阅读具有第二个延迟的元素,如:

 long diff = System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(1000L);
 NavigableSet<DelayedElement> set = queue.headSet(
 new DelayedElement(diff, "", null));
 //further processing goes here     

通过这种方式,我可以实现非阻塞性,并可以全速安全地从Collection中读取和读取。