javaconcurrencyjava.util.concurrent

is there a way to access / drain even the unexpired elements in a java DelayQueue


I have a use case where on the shutdown hook I want access all the elements in a DelayQueue even if the objects inside the queue are not yet expired (aka, you can't access them with poll, take or drainTo)

But on shutdown we are in this special use case where I just want to remove all the elements and write them to a store so one start up we can just re-add them to the queue.


Solution

  • If your Delayed items are scheduled in a ScheduledExecutorService, you can get a list of them when using shutdownNow():

    public class Test {
      public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        Runnable dummy = () -> System.out.println("Dummy operation");
    
        // ScheduledFuture implements Delayed
        ScheduledFuture<?> dummyHandle =
            scheduler.schedule(dummy, 10, TimeUnit.MINUTES);
        System.out.println("dummyHandle: " + dummyHandle);
    
        System.out.println("\nShutting down..");
    
        List<Runnable> myshotDownList = scheduler.shutdownNow();
        System.out.println("myshotDownList: " + myshotDownList);
      }
    }
    

    This will print:

    dummyHandle: java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5b94b04d[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@291caca8[Wrapped task = Test$$Lambda$185/0x00000001001b9440@385e9564]]
    
    Shutting down..
    myshotDownList: [java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5b94b04d[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@291caca8[Wrapped task = Test$$Lambda$185/0x00000001001b9440@385e9564]]]
    

    As you noticed when the Delayed item is in an accessible DelayQueue, like this:

        DelayQueue<Delayed> myDelayQueue = new DelayQueue<>();
        myDelayQueue.add(dummyHandle);
    

    poll() will return "null":

        System.out.println("Polling: " + myDelayQueue.poll());
    

    take() will wait until it's ready and remove it from the queue. Which in this case is a long 10 minute wait:

        try {
          System.out.println("Taking: " + myDelayQueue.take());
        } catch (InterruptedException ie) {
          System.out.println("Got interrupted");   
        }
    

    and drainTo() doesn't drain items not ready, so myDrainedList will be empty:

        List<Delayed> myDrainedList = new ArrayList<>();
        myDelayQueue.drainTo(myDrainedList);
    

    But since DelayQueue is a Collection, it can be used in the constructor of another Collection:

        List<Delayed> myDelayedList = new ArrayList<>(myDelayQueue);
    

    It can be also be iterated:

        List<Delayed> myDelayedList2 = new ArrayList<>();
        myDelayQueue.forEach(e -> myDelayedList2.add(e));
    

    Both the above lists will contain the Delayed element:

    java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5b94b04d[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@291caca8[Wrapped task = Test$$Lambda$185/0x00000001001b9440@385e9564]]