1

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?

assylias
  • 321,522
  • 82
  • 660
  • 783
RandomQuestion
  • 6,778
  • 17
  • 61
  • 97
  • Not sure if that would work since `Future` object would change after first scheduling. – RandomQuestion Jun 03 '14 at 01:40
  • Just tried too. Added `future.cancel` in same executor but it keeps running. – RandomQuestion Jun 03 '14 at 01:41
  • I have updated the question with updated code. But it does not seem to work. – RandomQuestion Jun 03 '14 at 01:44
  • Sorry i just saw that you reschedule within your task. I thought you were using the scheduleAtFixedRate method. – assylias Jun 03 '14 at 01:44
  • 1
    Reason I am not using `scheduleAtFixedRate` or 'scheduleWithFixedDelay` because if that condition becomes true, I want to stop execution. – RandomQuestion Jun 03 '14 at 01:45
  • It is bit tricky - one way would be to check a boolean `shouldRun` each time and setting that boolean to false after 5 secs. It needs to be volatile or an AtomicBoolean. But that is not exactly the same as cancelling the task. Keeping track of the future to cancel it is more complicated as it will probably require some locking. – assylias Jun 03 '14 at 01:54
  • try to create a timer for 5 seconds – Rod_Algonquin Jun 03 '14 at 03:23
  • I think you are looking for a self scheduling (polling) task, see http://stackoverflow.com/a/23231576/3080094 – vanOekel Jun 03 '14 at 14:14

1 Answers1

1

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.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • Makes sense. I was kind of inclined towards scheduling because it provides inherent waiting time with `schedule` method and didn't want to use `Thread.sleep` myself but yes it was getting really complicated. – RandomQuestion Jun 03 '14 at 18:45