我使用Java的DelayQueue在第二次延迟后发送事件。但问题是,在负载很重的情况下,我的消费者在DelayQueue上阻塞,直到来自另一个线程的相当大量的offer()操作都消失了。
有没有人知道Java中的非阻塞延迟队列实现?
答案 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中读取和读取。