OldCurmudgeon
OldCurmudgeon

Reputation: 65803

Why is my DelayQueue not delaying and printing in the wrong order?

I have the following supposedly trivially simple demo of a DelayQueue.

class DelayedThing implements Delayed {

    private final long waitUntil;
    private final String name;

    public DelayedThing(String name, long wait) {
        this.name = name;
        this.waitUntil = System.currentTimeMillis() + wait;
        System.out.println("DelayedThing(" + name + " wait=" + wait + " until-" + waitUntil);
    }

    @Override
    public long getDelay(TimeUnit unit) {
        System.out.println(name + " getDelay = " + unit.convert(waitUntil - System.currentTimeMillis(), TimeUnit.MILLISECONDS));
        return unit.convert(waitUntil - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        long diff = this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS);
        System.out.println(name + ".compareTo(" + o + ") = " + diff);
        return Long.signum(diff);
    }

    @Override
    public String toString() {
        return name;
    }
}

public void test() throws InterruptedException {
    BlockingQueue<Delayed> queue = new DelayQueue<>();
    queue.add(new DelayedThing("one second", 1000));
    queue.add(new DelayedThing("two seconds", 2000));
    queue.add(new DelayedThing("half second", 500));
    for (Delayed d : queue) {
        System.out.println(d);
    }
}

but it prints

half second
two seconds
one second

which is obviously wrong.

Upvotes: 4

Views: 224

Answers (1)

OldCurmudgeon
OldCurmudgeon

Reputation: 65803

The mistake is a subtle one. I am assuming that the iterator of a DelayQueue will perform a sequence of take calls for each element. Wrong!

See the iterator() JavaDoc:

Returns an iterator over all the elements (both expired and unexpired) in this queue.

This was quite unexpected.

One correct solution would be the following:

    while (queue.size() > 0) {
        System.out.println(queue.take());
    }

Note that this problem will also happen if you try to stream the queue:

    queue.stream().forEach((d) -> {
        System.out.println(d);
    });

As the streaming will happen on the iterator supplied by DelayQueue this will also have unexpected results.

Upvotes: 5

Related Questions