Core Java

Changing delay, and hence the order, in a DelayQueue

So I was looking at building a simple object cache that expires the objects after a given time. The obvious mechanism for this is the use the DelayedQueue class from the concurrency package in Java; but I wanted to know if it way possible to update the delay after an object has been added to the queue. Looking at the Delayed interface there didn’t seem to be a good reason not to in the docs so I thought it was time to experiment.

So first of all you need to to create an instance of Delayed, this is a very simple implementation that with the switch of a flag you can basically invert the timeout order in the list. (And add a suitable offset so things happen in the right order

static int COUNT=100;


   class DelayedSwap implements Delayed, Comparable<Delayed> {

       int index = 0;
       volatile boolean swap = false;
       long starttime;

       public DelayedSwap(int index, long starttime) {
           super();
           this.index = index;
           this.starttime = starttime;
       }

       private long getDelay() {
           return (swap ? starttime + (2*COUNT - index) * 100 :
               starttime + index * 100) - System.currentTimeMillis();
       }


       public String toString()
       {
           return index + ' swapped ' + swap + ' delay ' + getDelay();
       }

       @Override
       public long getDelay(TimeUnit unit) {
           return unit.convert(getDelay(), TimeUnit.MILLISECONDS);
       }

       @Override
       public int compareTo(Delayed delayed) {
           if (delayed == this)
               return 0;

           return (int)(getDelay(TimeUnit.MILLISECONDS) - delayed.getDelay(TimeUnit.MILLISECONDS));
       }
   }

So to test this I created a method that would create a bunch of the DelayedSwap objects and half way through processing the list switch the flag so altering the order of expiration.

public static void main(String[] args) throws InterruptedException {

       long start = System.currentTimeMillis();
       final List
            
               delayed = new ArrayList
             
               ();
       for (int i = 1; i < COUNT; i++) {
           delayed.add(new DelayedSwap(i, start));
       }

       final DelayQueue dq = new DelayQueue();
       dq.addAll(delayed);

       new Thread(new Runnable() {

           @Override
           public void run() {
               try {
                   TimeUnit.SECONDS.sleep(5);
               } catch (InterruptedException e) {
               }
               for (DelayedSwap d : delayed) {
                   d.swap = true;
               }
           }
       }).start();

       while (!dq.isEmpty()) {
           System.out.println(dq.take());
       }

   }

So what I was expecting was the elements 1-50 ish written out in the correct order but instead after the swap over the elements are coming out in an arbitrary order quite far away from the request delay time.

1 swapped false delay -19
2 swapped false delay -4
3 swapped false delay -4
4 swapped false delay -4
5 swapped false delay -4
6 swapped false delay -4
7 swapped false delay -4
8 swapped false delay -4
9 swapped false delay -4
10 swapped false delay -4
11 swapped false delay -4
12 swapped false delay -4
13 swapped false delay -4
14 swapped false delay -4
15 swapped false delay -4
16 swapped false delay -4
17 swapped false delay -4
18 swapped false delay -4
19 swapped false delay -4
20 swapped false delay -4
21 swapped false delay -4
22 swapped false delay -4
23 swapped false delay -4
24 swapped false delay -4
25 swapped false delay -4
26 swapped false delay -4
27 swapped false delay -4
28 swapped false delay -4
29 swapped false delay -4
30 swapped false delay -4
31 swapped false delay -4
32 swapped false delay -4
33 swapped false delay -4
34 swapped false delay -4
35 swapped false delay -4
36 swapped false delay -4
37 swapped false delay -4
38 swapped false delay -4
39 swapped false delay -5
40 swapped false delay -4
41 swapped false delay -4
42 swapped false delay -5
43 swapped false delay -4
44 swapped false delay -5
45 swapped false delay -5
46 swapped false delay -5
47 swapped false delay -5
48 swapped false delay -5
49 swapped false delay -5
50 swapped false delay -5
51 swapped true delay -6
94 swapped true delay -4306
96 swapped true delay -4506
87 swapped true delay -3606
91 swapped true delay -4006
97 swapped true delay -4606
95 swapped true delay -4406
98 swapped true delay -4706
92 swapped true delay -4106
82 swapped true delay -3106
80 swapped true delay -2906
90 swapped true delay -3906
93 swapped true delay -4206
74 swapped true delay -2306
99 swapped true delay -4806
70 swapped true delay -1906
69 swapped true delay -1806
66 swapped true delay -1506
83 swapped true delay -3206
62 swapped true delay -1107
61 swapped true delay -1007
58 swapped true delay -707
71 swapped true delay -2007
89 swapped true delay -3807
85 swapped true delay -3407
78 swapped true delay -2707
86 swapped true delay -3507
81 swapped true delay -3007
88 swapped true delay -3707
84 swapped true delay -3307
79 swapped true delay -2807
76 swapped true delay -2507
72 swapped true delay -2107
68 swapped true delay -1707
65 swapped true delay -1407
60 swapped true delay -907
57 swapped true delay -608
55 swapped true delay -408
75 swapped true delay -2408
77 swapped true delay -2608
73 swapped true delay -2208
63 swapped true delay -1208
67 swapped true delay -1608
64 swapped true delay -1308
59 swapped true delay -808
56 swapped true delay -508
54 swapped true delay -308
53 swapped true delay -208
52 swapped true delay -108
Process exited with exit code 0.

So the trick is when you know you are going to modify the delay is to remove and then re-add the element to the queue.

// Replacement swap loop
    for (DelayedSwap d : delayed) {
        if (dq.remove(d))
        {
            d.swap = true;
            dq.add(d);
        }
    }

This run produces a more sensible set of results:

1 swapped false delay -4
2 swapped false delay -8
3 swapped false delay -14
4 swapped false delay -8
5 swapped false delay -4
6 swapped false delay -4
7 swapped false delay -4
8 swapped false delay -4
9 swapped false delay -4
10 swapped false delay -4
11 swapped false delay -4
12 swapped false delay -4
13 swapped false delay -4
14 swapped false delay -4
15 swapped false delay -4
16 swapped false delay -4
17 swapped false delay -4
18 swapped false delay -8
19 swapped false delay -4
20 swapped false delay -4
21 swapped false delay -4
22 swapped false delay -4
23 swapped false delay -4
24 swapped false delay -4
25 swapped false delay -4
26 swapped false delay -4
27 swapped false delay -4
28 swapped false delay -4
29 swapped false delay -4
30 swapped false delay -4
31 swapped false delay -4
32 swapped false delay -4
33 swapped false delay -4
34 swapped false delay -4
35 swapped false delay -4
36 swapped false delay -4
37 swapped false delay -4
38 swapped false delay -4
39 swapped false delay -5
40 swapped false delay -5
41 swapped false delay -5
42 swapped false delay -4
43 swapped false delay -4
44 swapped false delay -5
45 swapped false delay -5
46 swapped false delay -5
47 swapped false delay -5
48 swapped false delay -5
49 swapped false delay -5
50 swapped false delay -5
99 swapped true delay -5
98 swapped true delay -5
97 swapped true delay -11
96 swapped true delay -1
95 swapped true delay -5
94 swapped true delay -9
93 swapped true delay -5
92 swapped true delay -5
91 swapped true delay -5
90 swapped true delay -5
89 swapped true delay -5
88 swapped true delay -5
87 swapped true delay -5
86 swapped true delay -5
85 swapped true delay -5
84 swapped true delay -5
83 swapped true delay -5
82 swapped true delay -5
81 swapped true delay -5
80 swapped true delay -5
79 swapped true delay -5
78 swapped true delay -5
77 swapped true delay -5
76 swapped true delay -5
75 swapped true delay -5
74 swapped true delay -5
73 swapped true delay -5
72 swapped true delay -6
71 swapped true delay -5
70 swapped true delay -5
69 swapped true delay -5
68 swapped true delay -5
67 swapped true delay -5
66 swapped true delay -5
65 swapped true delay -5
64 swapped true delay -5
63 swapped true delay -6
62 swapped true delay -5
61 swapped true delay -6
60 swapped true delay -6
59 swapped true delay -6
58 swapped true delay -6
57 swapped true delay -6
56 swapped true delay -6
55 swapped true delay -6
54 swapped true delay -6
53 swapped true delay -6
52 swapped true delay -6
51 swapped true delay -6
Process exited with exit code 0.

I don’t think this is a bug in the object itself, as you wouldn’t expect a HashTable to orders it’s self when the key changes, but I was a little bit surprise by the behaviour.

Happy coding and don’t forget to share!

Reference: Changing delay, and hence the order, in a DelayQueue from our JCG partner Gerard Davison at the Gerard Davison’s blog blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button