Question

Okay, so I have spent some time looking around but have not been able to find clear solution. I posted a separate question earlier but that is little bit different problem.

Problem: I want to poll for a condition to happen at periodically. If that condition is still false, again reschedule. If true, stop the scheduling. But I also want to wait only for some definitive amount of time. Here is what I wrote

 final ScheduledExecutorService service = Executors.newScheduledThreadPool(1);

  final Future<?> future = service.schedule(new Runnable() {
        @Override
        public void run() {
            if (conditionFalse()) {
                System.out.println("its false. Rescheduling");
                service.schedule(this, 2, TimeUnit.SECONDS);
            } else {
                System.out.println("true. Exiting");
            }
        }
    }, 2, TimeUnit.SECONDS);

   //Wait only for 5 seconds 
   future.get(5, TimeUnit.SECONDS); // does not work
   //does not work either
     service.schedule(new Runnable() {
        @Override
        public void run() {
            future.cancel(true);
        }
    }, 5, TimeUnit.SECONDS);

This keeps rescheduling itself until condition is met. Any suggestions on why it's not working? How do I wait only for 5 seconds and then stop the task execution?

Was it helpful?

Solution

When Future.get returns, your first task has been finished but possibly scheduled another task using the same Runnable. Invoking cancel on the first Future has no effect then as it represents the first (finished) task but not the new scheduled task. Keep in mind that each invocation of schedule will return a new Future.

In the end it’s not clear why you are making the task so complicated. It’s the big advantage of background threads that they can perform blocking operations without affecting the overall program execution. So if you want to recheck a condition every two seconds just create one single background task implementing the check-every-two-seconds logic. Waiting two seconds will block that thread but that’s ok, it’s a background thread.

ExecutorService service=Executors.newFixedThreadPool(1);
Future<?> f=service.submit(new Runnable() {
  public void run() {
    try {
      while(conditionFalse()) {
        System.out.println("it’s false, will wait");
        Thread.sleep(TimeUnit.SECONDS.toMillis(2));
      }
      System.out.println("true. exiting.");
    } catch(InterruptedException ex) {
      System.out.println("interrupted. exiting.");
    }
  }
});
try {
  try {
    f.get(5, TimeUnit.SECONDS);
    System.out.println("conditions met even before trying to cancel");
  } catch(TimeoutException ex) {
    System.out.println("canceling");
    System.out.println(f.cancel(true)?
      "canceled before conditions met": "conditions met before canceled");
  }
} catch(InterruptedException | ExecutionException ex) {
  throw new AssertionError(ex);
}

Canceling the task will end the sleep immediately which is what your question was about. As long as the conditions are not met and the task not canceled there is no need to reschedule something; the loop will just continue to run.

If you have more tasks, just raise the number of threads of the executor. It might feel like resource consumption but a sleeping thread does not consume much resources and the ScheduledExecutor would maintain a Thread sleeping between the executions of your task(s) as well. Your are even saving some operations when not rescheduling tasks.

What matters is whether the code is clear and understandable and I think, the simple loop wins over the (not even working) rescheduling code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top