I have a Spring Boot Application that uses CommandLineRunner and the Spring #Async annotation to run a method asynchronously. It all works fine, but when all of my threads complete, the application just hangs instead of exiting.
Here is a minimal example of what I have in my application:
Application.java:
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationStartup.java:
#Component
public class ApplicationStartup implements CommandLineRunner {
private final AsyncService asyncService;
#Inject
public ApplicationStartup(final AsyncService asyncService) {
this.asyncService = asyncService;
}
#Override
public void run(final String... strings) throws Exception {
//my logic is more complicated than this, but this illustrates my point
for (int i = 0; i < 1000; i++) {
asyncService.runAsyncMethod();
}
}
}
AsyncService.java:
#Service
#Transactional
public class AsyncService {
#Async
public void runAsyncMethod() {
//perform call to an API and process results
}
}
ExecutorConfig.java:
#Configuration
public class ExecutorConfig() {
#Bean
public ThreadPoolTaskExecutor asyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(64);
executor.setMaxPoolSize(64);
executor.setQueueCapacity(500);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("Scrub-");
executor.setKeepAliveSeconds(60);
executor.initialize();
return executor;
}
}
All of my threads make the call to runAsyncMethod() and every method call completes successfully, but then the application just hangs.
I tried changing some of the executor settings around. I didn't have the keepAliveSeconds at first, so I thought adding that would fix it, but it still hung after all threads were complete. I changed corePoolSize to 0, and that made the application exit when it was done, but it only used 1 thread the whole time.
Any ideas as to why the application is not exiting with the configuration above?
You missed to join the asynchronous jobs, that's why the run method exits (far) before all threads complete - and the awkward behavior is "more comprehensible".
According to doc, you could join like:
...
CompletableFuture<Void>[] myJobs = new CompletableFuture<>[N];
...
for (int i = 0; i < N; i++) {
myJobs[i] = asyncService.runAsyncMethod();
}
...
CompletableFuture.allOf(myJobs).join();
And your runAsyncMethod() would need to return a CompletableFuture<Void>. To do so, you can just return CompletableFuture.completedFuture(null);
Even if the marked as correct answer is valid. This is not full answer.
Without #EnableAsync and without WEB environment .web(WebApplicationType.NONE) the spring boot app automatically stop once started(as there is nothing to do/wait). So even if you don't do apringApp.close() in your app but only app.run(commandLine), the .close() method call automatically.
But once you added #EnableAsync - the behavior changes, as there might be async work, so app is not stopped once started. And if there is not stopping code, the app remain working (hangs).
For fixing this you need to do 2 things:
in the run method do wait all async work
implicitly call .close()after app started
Sample:
#EnableAutoConfiguration
#EnableAsync
public static class SpringApp extends SpringApplication {
#Bean
public TaskExecutor taskExecutor () {
return new SimpleAsyncTaskExecutor();
}
#Autowired
private Service service;
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event){
CompletableFuture<Void> aggregateFuture = service.doWork();
// avoid exiting this method before all job complected prevents app from hanging
aggregateFuture.join();
}
}
public static void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(SpringApp.class).web(WebApplicationType.NONE);
app.run()
.close(); // <--- THIS!
}
Related
I currently have a #Scheduled method in my Spring Boot app
#Async
#Scheduled(fixedDelay = 2000, initialDelay = 1000)
public void taskA() throws InterruptedException {
System.out.println("A - " + Thread.currentThread().getName());
}
My Spring Application file is as follows:
#SpringBootApplication
#EnableAsync
#EnableScheduling
public class SpringScheduleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringScheduleApplication.class, args);
}
}
I also have a config class set up to establish a thread pool to allow for parallel jobs
#Configuration
#EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {
public static final int NUM_PROCESSING_WORKERS = 10;
#Override
public void configureTasks(
ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
#Bean
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(NUM_PROCESSING_WORKERS);
}
}
When I run the scheduler, I can clearly see the job is picking up in the multiple thread pools.
A - pool-1-thread-7
A - pool-1-thread-3
A - pool-1-thread-9
....
Ok..... so here's my problem
What I WANT to do is create multiple instances of taskA and have that run in N amount of threads.
I know I can create N amount of methods with the #Scheduled annotation performing the same logic, but this is NOT an acceptable solution. This seems very redundant and looks bad, and don't want to copy paste if I add another thread to the pool. I want to be able to say something like:
for(int i = 0; i < SchedulingConfig.NUM_PROCESSING_WORKERS; i++){
taskA(); // I just want N amount of this tasks existing in parallel.
}
I cannot use anything like Quartz Scheduler because I want to be able to continue to use #Autowired in my project (I can get this behavior to work in Quartz, but setting up #Autowired is a pain and I would like to work with Spring Schedule annotations as much as possible)
You already wrote the answer yourself. You already have the for loop to use. Put the #Scheduled on the method with the for-loop, which calls a taskA method in an external class, and which has the #Async annotation.
#Autowired
private External external
#Scheduled
public void scheduler() {
for(int i = 0; i < SchedulingConfig.NUM_PROCESSING_WORKERS; i++){
external.taskA(); // I just want N amount of this tasks existing in parallel.
}
}
public class External {
#Async
public void taskA() { ... }
}
Another solution is to inject the AsyncTaskExecutor into your class that does the scheduling and call submit to execute the task.
#Autowired
private AsyncTaskExecutor taskExecutor;
#Scheduled
public void scheduler() {
for(int i = 0; i < SchedulingConfig.NUM_PROCESSING_WORKERS; i++){
taskExecutor.submit( () -> taskA());
}
}
The advantage of this is that it is more explicit and that your taskA method can be in the same class.
I created a simple spring boot application with scheduled (#Scheduled) task. In that scheduled task, I would like to call async function with #Async, but I can see it still runs on the scheduling thread without switch to another thread. I also tried to customise executor, but no luck. Here are some codes.
I also already enable async in main class
public class scheduledService {
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
AsyncService service = new AsyncService();
service.asyncCall();
service.asyncCall();
service.asyncCall();
asyncCall();
}
}
public class AsyncService {
#Async()
public void asyncCall(){
System.out.printly("Current thread -- {}",Thread.currentThread().getName()))
Thread.sleep(10000);
}
}
#Bean(name = "MyThreadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyThreadPoolExecutor-");
executor.initialize();
return executor;
}
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class ScheduledApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ScheduledApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
according to Baeldung:
#Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won't work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly.
so you can put your async method in a service and use it from there
you need to autowire AsyncService, do not create new object like
AsyncService service = new AsyncService();
Also, annotate your scheduledService class with #Service or #Component
#Service
public class scheduledService {
#Autowired
private AsyncService service ;
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
service.asyncCall();
service.asyncCall();
service.asyncCall();
}
}
不要在同一个类中调用异步方法
Do not call asynchronous methods in the same class.
将异步任务单独放到一个类 并且在这个类上加上#Component
Put asynchronous tasks into a single class and add # component to this class
Use #EnableAsync on the top of class where you are creating async bean, not on the ScheduledApplication.
I am having a hard time understanding the issue at hand, which I believe is a problem with the way how Spring proxies get created.
In this minimal example, I have two classes, AccountLoader and BankImpl, which implements an interface Bank. Upon start-up, AccountLoader executes some concurrent calls to an autowired Bank-instance, where the method in BankImpl is advised with an aspect.
In this setup the call to complete the future (Future.get) finishes with a TimeoutException, because the call appears to never terminate. However, if I call the same method before the callables get submitted to the executor, all calls finish successfully.
What is going on with Spring here? Why does this async call not terminate? And why in all seven hells does it terminate, if I add a synchronous call before the async one?
You may find the code below, a complete working example is also available on Github
public interface Bank {
Map<String, String> getAccounts(String q);
}
The simple implementation
#Service
public class BankImpl implements Bank {
private static final Logger LOGGER = LoggerFactory.getLogger(BankImpl.class);
#Override
public Map<String, String> getAccounts(String q) {
LOGGER.info("Listing accounts for {}", q);
return Collections.singletonMap(q, "q");
}
}
And finally the caller
#Service
public class AccountLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountLoader.class);
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
private Bank bank;
#PostConstruct
public void refresh() {
LOGGER.info("Refreshing accounts");
// Uncommenting the following line will let the calls terminate
// bank.getAccounts("sync");
try {
executorService.submit(() -> { bank.getAccounts("async"); })
.get(5L, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
For the sake of completeness, here are the aspect
#Aspect
#Component
public class SomeAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeAspect.class);
#AfterReturning(pointcut = "execution(* com.github.mtritschler.aspects.BankImpl.getAccounts(..))", returning = "returnValue")
public Map<String, String> logCallee(Map<String, String> returnValue) {
LOGGER.info("Result is {}", returnValue);
return returnValue;
}
}
and last but not least the configuration
#EnableAspectJAutoProxy
#Configuration
public class MyConfig {
}
Update: if I remove the #EnableAspextJAutoProxy, I also don't get an exception. Switching to load-time weaving did not change anything either.
It turned out that there was a race condition between the application initialization in the main thread and the concurrent access to the injected dependency.
Once we switched the #PostConstruct for a listener on ContextRefreshedEvent it worked just fine.
I have a method that will be rarely called. This method collect garbage in db. I don't want to make user to wait for server response so i decided to call this method from new thread from my service layer. i'm using Spring.
Service class:
#Service
#Transactional
public class UploadService {
#Resource(name = "UploadDAO")
private UploadDao uploadDao;
Method that i don't want to wait for
public void collectBlobGarbage(){
Thread th = new Thread(new Runnable() {
#Override
public void run() {
uploadDao.collectBlobGarbage();
}
});
th.start();
}
Is it a good way to do like this?
If you have Spring on your classpath you might as well use #Async
#Async
public CompletableFuture<Void> collectBlobGarbage() throws InterruptedException {
CompletableFuture.completeFuture(uploadDao.collectBlobGarbage());
}
On your main class you need to use #EnableAsync like:
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
And you need an executor bean:
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Stackoverflow-");
executor.initialize();
return executor;
}
I think the provided solution can potentially cause a lot of threads on you server.
As an alternative, you can consider using Executors.newSingleThreadExecutor in such a way that you will get an executor service that is limited only to one thread—so you will never create more than one thread—and that is what you need.
Also as you are using Spring, consider configuring a SingleThreadExecutor instantiation as a separate bean—in such a way that you will be able to change the implementation of ExecutorService in the future.
Using Spring Boot 1.5.2.RELEASE and the #Async annotation seems to be ignored.
Have setup the environment like this:
#SpringBootApplication
#EnableAsync
public class Application extends AsyncConfigurerSupport {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("async-task-");
executor.initialize();
return executor;
}
...
... the async method itself:
#Service
public class MyService {
#Async
public Future<Long> asyncTask() throws Exception {
Long test = 1023L;
Thread.sleep(10000);
return new AsyncResult<>(test);
}
}
... now I'm trying to use this:
#RestController
public MyController {
#Autowired
public MyService myService;
#PostMapping("/test")
public ResponseEntity<MyResponse> test() {
return new ResponseEntity<>(
new MyResponse(myService
.asyncTask()
.get()),
HttpStatus.OK);
}
}
... and the controller method still hangs for 10sec instead of to be immediatelly returned.
The #Async method is called from the different object. It's neither private nor transactional one as it mentioned at the similar questions.
How to let the method to be invoked asynchronously?
You should have a look at the
Future#get javadoc:
Waits if necessary for the computation to complete, and then retrieves
its result.
You are transforming your async method to a synchronous call by calling get method.
Thus instead of calling get, just return the Future. Spring MVC supports future as return type:
A ListenableFuture or CompletableFuture/CompletionStage can
be returned when the application wants to produce the value from a
thread pool submission.
Example:
return myService.asyncTask().thenApply(r -> ResponseEntity.ok(new MyResponse(r)));
Your test() function is invoking get() on the Future instance. The documentation for this function states: "Waits if necessary for the computation to complete, and then retrieves its result."
So rather than invoking get(), you likely want to return some sort if ID that the caller can use to retrieve the result at a later time (or switch to a synchronous response).