17

I want to be able to cancel a method marked with the @Async annotation by it's future.

I have a Spring method marked with the @Async annotation. This method does some computation, and eventually returns a result. All examples I have seen recommend using the AsyncResult class to return this Future.

@Async
public Future<String> run() {
    // ... Computation. Minutes pass ...
    return new AsyncResult<String>("Result");
}

I call the following method, from another Component, in the following manner. For example purposes, I wish to immediately cancel this thread:

Future<String> future = component.run();
future.cancel(true);

In this case, the thread will never be cancelled. This is because, looking at the Spring implementation for AsyncResult here: https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java#L71 , this method doesn't actually do a single thing. It simply returns false, that the thread could not be cancelled. This is my problem. How can I cancel the Thread being created by the @Async method? I have no access to the internal thread being created by Spring - so I have no means myself with which to cancel it, do I?

Patrick D
  • 6,659
  • 3
  • 43
  • 55
  • check those 2 answers [here](http://stackoverflow.com/questions/21084195/spring-async-cancel-and-start) and [here](http://stackoverflow.com/questions/35798987/stop-an-async-spring-method-from-caller-class) , or else create a custom `ThreadPoolExecutor` to handle the `@Async` annotated methods or pass a custom `AsyncResult` which will bind with the running task. – AntJavaDev Aug 10 '16 at 17:42
  • 1
    The answers in those questions seem to describe creating some flag/semaphore in order to tell the running thread to stop processing at a later point. This is different than terminating a thread as the Future.cancel() method would do in a self defined `Runnable`. Can you explain a bit further what a custom `ThreadPoolExecutor` would look like? I also define my own Executor, so adding to it would not be difficult or inconvenient. – Patrick D Aug 10 '16 at 17:52

1 Answers1

14

Actually, Spring @Async uses a background thread pool of it's own. And as long as cancel() method of Future or shutdownNow() of executor service is concerned, Calling executor.shutdownNow() or future.cancel(true) don’t stop the ongoing thread immediately. What these methods do is simply call .interrupt() on the respective thread(s). And interrupts has no guaranteed immediate effect. It issues a flag so whenever in sleeping or waiting state, the thread will be stopped.

To be noted, If your tasks ignore the interruption, executor.shutdownNow() and future.cancel(true) will behave exactly the same way as executor.shutdown() and future.cancel(false).

If you need a way to stop the slow or blocking operation. If you have a long/endless loop, you can just add a condition whether Thread.currentThread().isInterrupted() and don’t continue if it is true(end the operation).

  • 1
    Great. I added boolean checks for `Thread.interrupted()` throughout my `@Async` method's logic; and if I detect the interrupt, I can safely tear down the method and cancel execution. – Patrick D Aug 11 '16 at 13:34
  • 1
    Hi Prateek. Can you please provide a source for your answer? – George Aristy Apr 11 '18 at 19:03
  • what happens if all the threads in the pool are leaked, and it ends up empty? – Jason Oct 30 '20 at 20:42
  • @PatrickD facing same issue.. Can you have look at the problem https://stackoverflow.com/questions/71708378/gracefully-terminating-existing-async-thread-in-spring-boot – Utkarsh Saraf Apr 01 '22 at 17:52