Spring Boot #Async is called Synchronously [duplicate] - spring

This question already has an answer here:
Spring 3.x - #Async methods not called concurrently by task executor
(1 answer)
Closed 7 years ago.
I have a long running service method which I'm starting with a rest call from the controller.
Service:
#Service
public class TestServiceImpl implements TestService {
#Override
public void doSomething() {
System.out.println("1: " + DateTime.now());
runLongTask();
System.out.println("2: " + DateTime.now());
}
#Async
private runLongTask() {
System.out.println("Test");
Thread.sleep(10000);
System.out.println("3: "+ DateTime.now());
}
}
In my Application.java I have
private int poolSize = 10;
private int queueCapacity = 10;
#Bean(name="taskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(this.poolSize);
taskExecutor.setQueueCapacity(this.queueCapacity);
taskExecutor.afterPropertiesSet();
return new ThreadPoolTaskExecutor();
}
And of course I have the #EnableAsync Annotation at my Application.java class
So I would Expect that the output is something like that:
1: 2015-10-23 11:20:00
2: 2015-10-23 11:20:01
Test
3: 2015-10-23 11:20:11

you need to call method from different class to use annotations like #Async or #Transactional. Spring do this stuff by wrapping annotated methods by proxy. In case when you call method from the same class then it has no effect.

According to the documentation you need to implement the AsyncConfigurer interface in your Configuration class (in this case I assume is your Application instance) to override the SimpleAsyncTaskExecutor.
A full example is also shown in the documentation:
#Configuration
#EnableAsync
public class AppConfig implements AsyncConfigurer {
#Bean
public MyAsyncBean asyncBean() {
return new MyAsyncBean();
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return MyAsyncUncaughtExceptionHandler();
}
}
HTH,
Jose Luis

Related

Spring Boot 3 context propagation in micrometer tracing

Spring Boot 3 has changed context propagation in tracing.
https://github.com/micrometer-metrics/tracing/wiki/Spring-Cloud-Sleuth-3.1-Migration-Guide#async-instrumentation
They deliver now library to this issue. I guess I don't quite understand how it works.
I have created a taskExecutor as in guide.
#Bean(name = "taskExecutor")
ThreadPoolTaskExecutor threadPoolTaskScheduler() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor() {
#Override
protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
ExecutorService executorService = super.initializeExecutor(threadFactory, rejectedExecutionHandler);
return ContextExecutorService.wrap(executorService, ContextSnapshot::captureAll);
}
};
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
And I have marked #Async like this:
#Async("taskExecutor")
public void run() {
// invoke some service
}
But context is not propagated to child context in taskExecutor thread.
I was facing the same problem. Pls add this code to the configuration and everything works as expected.
#Configuration(proxyBeanMethods = false)
static class AsyncConfig implements AsyncConfigurer, WebMvcConfigurer {
#Override
public Executor getAsyncExecutor() {
return ContextExecutorService.wrap(Executors.newCachedThreadPool(), ContextSnapshot::captureAll);
}
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(new SimpleAsyncTaskExecutor(r -> new Thread(ContextSnapshot.captureAll().wrap(r))));
}
}

Implement TaskExecutor in spring boot where Scheduling is already defined

I have already define Scheduler in my spring boot project. Then I have to implement task scheduler which run thread pool. After I have added my scheduler task won't triggered. Even test case didn't run.
Scheduler Config Class
#Configuration
#EnableScheduling
public class SchedulerConfig {
}
Thread pool config class
#Configuration
#EnableAsync
public class AsyncConfig {
private ThreadPoolTaskExecutor executor;
#Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
executor.setMaxPoolSize(500);
executor.initialize();
return executor;
}
}
#Async("threadPoolTaskExecutor")
public void smsProcessor(List<CampaignSchedulerData> campaignSchedulerDataList, int tenantId) throws InterruptedException {
// file reader from AWS and send message accordingly
}
Scheduler task
#Scheduled(cron = "${cron-expression.file-write}")
public void fileWriteScheduler() {
LocalDateTime startDateTime = LocalDateTime.now();
log.info("Run file Write Scheduler. Start time is :: {} ",
DateTimeUtil.DATE_TIME_FORMATTER.print(startDateTime));
//add config scheduler time
schedulerService.processFileWriteScheduler(startDateTime.minusMillis(file_write_SchedulerWaitingTime));
LocalDateTime endDateTime = LocalDateTime.now();
log.info("Run file Write Scheduler. ", "End time is :: {} ",
DateTimeUtil.DATE_TIME_FORMATTER.print(endDateTime), ". Time taken :: {} ", Period.fieldDifference(startDateTime, endDateTime));
}
I have tried to implement task scheduler then ApplicationSpringBoot class won't run.
#SpringBootApplication
#EnableFeignClients
//Enable Annotations
public class BusinessTemplateApplication {
}
I figured this out.
When I add the task scheduler to the SchedulerConfig class it will resolved
#Configuration
#EnableScheduling
public class SchedulerConfig {
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(500);
threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
}

How to use Async in Spring Boot?

Below is my code.
With my below code, different thread ids are not getting created.
The output has same thread id.
#Controller
#RequestMapping(value = "/Main")
public class MyController
{
#Autowired
private MyService myService;
#PostMapping("/Sub")
#ResponseBody
public String readInput(#RequestBody String name)
{
for (int i = 0;i<5;i++)
{
myService.asyncMethod();
}
return "Success";
}
}
With my below code, different thread ids are not getting created.
#Repository
#Configuration
#EnableAsync
public class MyService {
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
#Async("threadPoolTaskExecutor")
public void asyncMethod() {
System.out.println("Thread " + Thread.currentThread().getId()+ " is running");
}
}
First of all, it is impossible to judge whether the thread pool is used by the thread id. You can set the thread prefix and judge by the log
Configure thread pool
#Slf4j
#Configuration
public class ThreadExecutorConfig {
#Autowired
private ThreadPoolProperties threadPoolProperties;
#Bean(name = "taskExecutor")
public ExecutorService executorService() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(threadPoolProperties.getCorePoolSize());
executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize());
executor.setQueueCapacity(threadPoolProperties.getQueueSize());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("myThread-");
executor.initialize();
log.info("threadPoolConfig;corePoolSize:[{}];maxPoolSize:[{}];queueSize:[{}]",
threadPoolProperties.getCorePoolSize(),
threadPoolProperties.getMaxPoolSize(),
threadPoolProperties.getQueueSize());
return executor.getThreadPoolExecutor();
}
}
Use #Async annotations on methods
#Async(value = "taskExecutor")
#Override
public void asyncSave(OperationLogModel entity) {
if (log.isDebugEnabled()) {
log.debug("thread:{};entity:{}", Thread.currentThread().getName(), entity.toString());
}
entity.setCreateTime(LocalDateTime.now());
super.save(entity);
}
View log
Good question! The answer is in ThreadPoolTaskExecutor. Its default corePoolSize is one.
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(3);//or any (positive) integer that suits you.
return threadPoolTaskExecutor;
}
..will behave more as we expect:
Thread 127 is running
Thread 128 is running
Thread 128 is running
Thread 129 is running
Thread 127 is running

Best approach to allocate dedicated background thread in Spring Boot

I need to create a dedicated thread listening on DatagramSocket.
Old school approach would be to create one during context creation:
#Bean
void beanDef() {
var thread = new Thread(myRunnable);
thread.setDaemon(true);
thread.start();
}
More modern approach would be to create an executor:
#Bean
Executor() {
var executor = Executors.newSingleThreadExecutor();
executor.submit(myRunnable);
}
Which one should I prefer?
Something like this would be a modern way to launch a background thread in Spring:
#Component
public class MySocketListenerLauncher {
private ExecutorService executorService;
#PostConstruct
public void init() {
BasicThreadFactory factory = new BasicThreadFactory.Builder()
.namingPattern("socket-listener-%d").build();
executorService = Executors.newSingleThreadExecutor(factory);
executorService.execute(new Runnable() {
#Override
public void run() {
// ... listen to socket ...
}
});
executorService.shutdown();
}
#PreDestroy
public void shutdown() {
if (executorService != null) {
executorService.shutdownNow();
}
}
}
Better use #Async annotation to create a thread in the background.
The #EnableAsync annotation switches on Spring's ability to run #Async methods in a background thread pool. This class also customizes the used Executor.
#Configuration
#EnableAsync
public class ThreadConfig {
#Bean(name = "specificTaskExecutor")
public TaskExecutor specificTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
return executor;
}
}
and in below snippet
#Async("specificTaskExecutor")
public void runFromAnotherThreadPool() {
System.out.println("You function code here");
}
I hope this will help you.

#Scheduled and #Async are sharing same threadpool in spring-boot

I have configured two different thread pools, one for #Scheduled and other for #Async. However, I notice that the thread-pool for #Async is not being used.
Here is the Scheduler configuration
#Configuration
#EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("my-sched-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
Here is the Configuration for Async
#Configuration
#EnableAsync
public class AppConfig {
#Bean(name = "asyncTaskExecutor")
public TaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setMaxPoolSize(15);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("my-async-pool-");
executor.initialize();
return executor;
}
}
Here is how I invoke them
#Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
asyncMethod();
}
#Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
Here are the logs
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message from schedule"}
{"thread":"my-sched-pool-1","level":"INFO","description":"here is the message from async"}
As you can notice, both logs are having same pool of that scheduler. but I expect to see the second one to come from async
If you call #Async methods from the same class they are declared you are effectively bypassing Spring's proxy mechanism and that is why your example is not working. Try calling the method from a separate class annotated with #Service or any of the other #Component types.
#Service
SomeScheduledClass {
private final SomeAsyncClass someAsyncClass;
public SomeScheduledClass(SomeAsyncClass someAsyncClass) {
this.someAsyncClass = someAsyncClass;
}
#Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
someAsyncClass.asyncMethod();
}
}
#Service
SomeAsyncClass {
#Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
}

Resources