Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine when a ScheduledExecutorService will fire next

Tags:

java

android

Is there a way to determine the current millisecond or other time measure of when a ScheduledExecutorService is going to fire next?

scheduleTaskExecutorUpdate = Executors.newSingleThreadScheduledExecutor();

I have a longer running ScheduledExecutorService(A) and from a shorter running ScheduledExecutorService(B) I would like to update a TextView, display a countdown of when ScheduledExecutorService(A) is going to fire next.

like image 897
CampbellGolf Avatar asked Aug 31 '12 21:08

CampbellGolf


1 Answers

If you keep track of the ScheduledFutures for all tasks scheduled with the executor, then yes. This becomes a problem of determining the minimum delay until the next task must fire, which should be a fairly reliable estimate.

final Collection<ScheduledFuture<?>> futures = ...;
/* for each schedule, add it to the above collection */
...
final long delay = Collections.min(futures).getDelay(TimeUnit.MILLISECONDS);

... or, for one task, you merely do:

final ScheduledFuture<?> future = ...;
final long delay = future.getDelay(TimeUnit.MILLISECONDS);

Now, if you're going to be doing it a lot, with mutiple tasks, I'd suggest you maintain a DelayQueue. However, you can't merely throw the ScheduledFutures in the queue without maintaining the changes caused by periodic tasks. Luckily, the class ScheduledThreadPoolExecutor should handle this nicely via its decorateTask methods.

Note this means you will need to create your ownScheduledThreadPoolExecutor directly. Something like the below might work.

public class TrackingSingleThreadScheduledExecutor
    extends ScheduledThreadPoolExecutor {

  private final DelayQueue<ScheduledFuture<?>> tasks
      = new DelayQueue<RunnableScheduledFuture<?>>();

  public TrackingSingleThreadScheduledExecutor() {
    super(1);
  }

  public DelayQueue<? extends ScheduledFuture<V>> tasks() {
    return tasks;
  }

  public ScheduledFuture<V> next() {
    return tasks.peek();
  }

  protected <V> RunnableScheduledFuture<V> decorateTask
      (final Callable<V> callable, final RunnableScheduledFuture<V> task) {
    return new QueueAwareTask(task);
  }

  protected <V> RunnableScheduledFuture<V> decorateTask
      (final Runnable runnable, final RunnableScheduledFuture<V> task) {
    return new QueueAwareTask(task);
  }

  private final class QueueAwareTask<V> implements RunnableScheduledFuture<V> {

    private final RunnableScheduledFuture<V> inner;

    public QueueAwareTask(final RunnableScheduledFuture<V> inner) {
      this.inner = inner;
    }

    public boolean isPeriodic() {
      return inner.isPeriodic();
    }

    public long getDelay(final TimeUnit unit) {
      return inner.getDelay(unit);
    }

    public void run() {
      inner.run();
      if (queue.remove(inner) && inner.isPeriodic()
          && !inner.isCancelled()) {
        queue.add(inner);
      }
    }

    public int compareTo(final Delayed other) {
      return inner.compareTo(other);
    }

    public boolean cancel(final boolean mayInterruptIfRunning) {
      final boolean cancelled = inner.cancel(mayInterruptIfRunning);
      if (cancelled) {
        queue.remove(inner);
      }
      return cancelled;
    }

    public boolean isCancelled() {
      return inner.isCancelled();
    }

    public boolean isDone() {
      return inner.isDone();
    }

    public V get() throws InterruptedException, ExecutionException {
      return inner.get();
    }

    public V get(final long timeout, final TimeUnit unit)
        throws InterruptedException, ExecutionException {
      return inner.get(timeout, unit);
    }
  }
}

Then, usage is as follows.

final TrackingSingleThreadScheduledExecutor executor
    = new TrackingSingleThreadScheduledExecutor();
...
final long delay = executor.next().getDelay(TimeUnit.MILLISECONDS);
like image 99
obataku Avatar answered Nov 01 '22 03:11

obataku