I have a timed task that will make an http call and then check the return result. If the result is a certain value, I will do some stuff, and then this timed task can be terminated and no longer needs to be scheduled. How can I cancel this schedule within my task? ? Does ScheduledExecutorService provide a parameter similar to the termination condition?
ScheduledExecutorService service = new ScheduledThreadPoolExecutor(5);
service.scheduleAtFixedRate(() -> {
// http request
// check
// result is xxx, no more schedule
// How can I stop this task schedule within my task? ?
}, 1, 60, TimeUnit.SECONDS);
Make a non-scheduled executor service.
Make a scheduled executor service.
Instantiate your repeating task as a Runnable
or Callable
. Pass to its constructor a reference to both executor services.
Schedule your task on the scheduled executor service.
Every time the task runs, check for your quit condition.
run
/ call
method complete.run
/ call
method of that task cancels the passed scheduled executor service. To do the canceling, the task calls ScheduledExecutorService#shutdown
and #awaitTermination
.
The simplest thing that can possibly work is to throw an exception from the body of the scheduled function when you want to stop. I think it's ugly.See the javadoc here .
Another way is not to schedule periodically (ie not use scheduleAtFixedRate), but schedule just once and reschedule (or not) after the result of the call is checked.
You will have to keep track of how much time the call took if you want to approximate what scheduleAtFixedRate does.
Note also that using scheduleAtFixedRate, if your http call takes more than the interval you specified and your ScheduledExecutorService has some parallelism (you gave it 5 threads) you will have multiple concurrent execution of the http call. This might or might not be what you want.
This solution approximates scheduleAtFixedRate and degrades to scheduleAtFixedDelay when your call takes more than 60 seconds:
private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public static void main(String[] args) {
scheduler.schedule(() -> {
performHttpCallAndScheduleAgainIfNeeded(scheduler);
}, 1, TimeUnit.SECONDS);
}
private static void performHttpCallAndScheduleAgainIfNeeded(ScheduledExecutorService scheduler) {
final long startTime = System.currentTimeMillis();
boolean callWasOk = performHttpCall();
if (!callWasOk) {
final long elapsed = System.currentTimeMillis() - startTime;
final long millisToWaitForNextAttempt = Math.max(0, 60000 - elapsed);
scheduler.schedule(() -> performHttpCallAndScheduleAgainIfNeeded(scheduler), millisToWaitForNextAttempt, TimeUnit.MILLISECONDS);
} else {
// the call was ok... you have nothing to do.
}
}
private static boolean performHttpCall() {
// do whatever you need to do here...
}
If you instead still want to use scheduleAtFixedRate or scheduleWithFixedDelay, you have to keep some state around (beware of concurrency) and note that both methods return a future that you have to use to cancel them.
The trick here is that you will have to pass the result of your scheduleAtFixRate call to the actual scheduled function. You can solve this chicked-egg problem by using a container instead of the value: here I used CompletableFuture as a container:
public static void main(String[] args) {
final CompletableFuture<ScheduledFuture<?>> cancellablePeriodicTask = new CompletableFuture<>();
final ScheduledFuture<?> cancellable = scheduler.scheduleAtFixedRate(() -> {
performHttpCallAndScheduleAgainIfNeeded(cancellablePeriodicTask);
}, 1, 60, TimeUnit.SECONDS);
cancellablePeriodicTask.complete(cancellable);
}
private static void performHttpCallAndScheduleAgainIfNeeded(CompletableFuture<ScheduledFuture<?>> cancellable) {
boolean callWasOk = performHttpCall();
if (callWasOk) {
// here you cancel
cancellable.whenComplete((scheduledFuture, throwable) -> {
if (throwable == null) {
scheduledFuture.cancel(true);
}
});
}
// call was not ok here, nothing to do since the scheduler will call this again.
}
private static boolean performHttpCall() {
// do whatever you need to do here...
}
In this approach CompletableFuture as mechanism for passing values between threads is not the only thing that works (SynchronousQueue also works here).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.