I'm trying to use ExecutorCompletionService and ScheduledExecutorService together.
What I need to do is schedule disparate activities that each have "delays before execution" and then "reschedule them" (which different delays) based upon the result of the last run."
The problem that I have is that I cannot use ExcecutorCompletionService submit with a "delay"
I tried the following, but it blocks forever...
Obviously I'm missing a fundamental issue in the Java Language.
Is there anyway to schedule tasks to the ScheduledExecutorService such that the CompletionService "knows about it"?
public class Bar {
private ScheduledExecutorService scheduledExecutor;
private Future<Status> action1Future;
private Future<Status> action2Future;
private ExecutorCompletionService<Status> pool;
private long delay1 = 10;
private long delay2 = 20;
private long delay3 = 30;
public void start() {
scheduledExecutor = Executors.newScheduledThreadPool(3);
Action1 a1 = new ActionOne(); // Action1 implements Callable<Status>
Action2 a2 = new ActionTwo(); // Action2 implements Callable<Status>
pool = new ExecutorCompletionService<Status>(scheduledExecutor);
action1Future = scheduledExecutor.schedule(a1, delay1, TimeUnit.SECONDS);
action2Future = scheduledExecutor.schedule(a2, delay1, TimeUnit.SECONDS);
monitorAndRestart();
}
private void monitorAndRestart() {
boolean isDone=false;
do {
try {
// THIS IS WHERE IT BLOCKS.
Future<Status> processedItem = pool.get();
if (processedItem == action1Future) {
if (processedItem.get() == Status.GOOD) {
action1Future = scheduledExecutor.schedule(new ActionOne(), delay1, TimeUnit.SECONDS);
} else {
action1Future = scheduledExecutor.schedule(new ActionOne(), delay2, TimeUnit.SECONDS);
}
} else if (processedItem == action2Future) {
if (processedItem.get() == Status.GOOD) {
action1Future = scheduledExecutor.schedule(new ActionOne(), delay2, TimeUnit.SECONDS);
} else {
action1Future = scheduledExecutor.schedule(new ActionOne(), delay3, TimeUnit.SECONDS);
}
}
} catch (InterruptedException e) {
isDone = true;
// handle this.. shudown whatever
}
catch (ExecutionException e) {
// handle this
}
} while (isDone == false);
}
public static void main(String[] args) {
Bar myRunner = new Bar();
myRunner.start();
}
}
If I put the "delay in the Callable" create the callable via new ActionOne(delay); and use CompletionService.submit(..) It works.
actionFuture1 = pool.submit(new ActionOne(delay1));
/////
public class ActionOne implements Callable<Status>(
private final delay;
public ActionOne(long dl) {
delay=dl;
}
Status call() {
try {
Thread.sleep(delay * 1000); // seconds
return doSomething()
} catch (...) { //thread.sleep execptions}
}
}
So I guess the final question is the following: Is there something fundamentally better about ScheduledExecutorService over the Thread.sleep(delay) way of doing this?
We do this sort of rescheduling in one of our apps, and we settled on wrapping the Task like Ed Thomas suggested. (We also made the rescheduling super flexible by passing an iterator that returns the next execution time for the task - allows us to use many different scheduling strategies).
Another option is to subclass ScheduledThreadPoolExecutor and override afterExecute. This may ultimately be cleaner than wrapping the Task.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With