I have an async task executor.
How can I tell Spring to wait on application shutdown until all tasks are finished?
#Bean
public ThreadPoolExecutor singleExecutor() {
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(10),
new ThreadPoolExecutor.DiscardPolicy());
}
#Service
class MyService {
#Async("singleExecutor")
public void runAsync() {
}
}
Solution
The org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor class (implements the org.springframework.core.task.TaskExecutor interface) supports the requested functionality by the the following methods:
setWaitForTasksToCompleteOnShutdown().
setAwaitTerminationSeconds().
Therefore, please, consider migrating the bean:
From: java.util.concurrent.ThreadPoolExecutor.
To: org.springframework.core.task.TaskExecutor.
«Migrated bean» example
#Bean
public TaskExecutor singleExecutor() {
final ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(1);
taskExecutor.setMaxPoolSize(1);
taskExecutor.setKeepAliveSeconds(0);
taskExecutor.setQueueCapacity(10);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(300);
return taskExecutor;
}
ThreadPoolTaskExecutor class: Queue type
Please, see the Javadoc for the ThreadPoolTaskExecutor.createQueue() method on the default queue factory method behaviour:
protected BlockingQueue<Runnable> createQueue(int queueCapacity)
Create the BlockingQueue to use for the ThreadPoolExecutor.
A LinkedBlockingQueue instance will be created for a positive capacity value; a SynchronousQueue else.
It is possible to override the behaviour by using a class derived from the ThreadPoolTaskExecutor class with the overridden factory method.
For example:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
public final class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
#Override
protected BlockingQueue<Runnable> createQueue(final int queueCapacity) {
return new ArrayBlockingQueue<>(queueCapacity);
}
}
Related
I'm using #EnableAsync and #Async for enabling asynchronous call when submitting request to thread pool. This work well, as it doesn't block the flow at future.get(). Now, we added another bean processExecutor within MySpringBootApplication class. But for this we wanted to wait till it completes all the task when submitted to this executor. But actually, it continues and doesn't wait at future.get(). Can someone help me, what can I make change so that it waits for all thread to complete ?
#SpringBootApplication
#EnableAsync
public class MySpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(MySpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
#Bean(name="calculateExecutor")
public TaskExecutor calExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(MySpringBootApplication.class,args);
}
}
Following is Service class
#Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public Future<String> process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
//API call which takes 2-3 seconds to complete
return new AsyncResult<String>(resp.getBody().getDataId())
}
}
I'm calling future.get() from other component class.
I've configuration that is implementing AsyncConfigurer as bellow:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(5);
executor.setCorePoolSize(5);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
That new thread pool is working fine with that:
#Component
public class MessageJmsListener {
#Async
#EventListener
public void onApplicationEvent(CustomMessage messageJmsWrapper) {
log.info("Current thread: {}", Thread.currentThread().getName());
}
#Async
#EventListener
public void onApplicationEventSecond(SecondCustomMessage messageJmsWrapper) {
log.info("Second Listener thread: {} , Thread.currentThread().getName());
}
}
And I would like to achieve such an effect that for the first listener I give a separate bean (number of threads) and for the second. Is such a thing possible?
Thanks in advance!
Best Regards
It looks like that using qualifiers could help you.
Create two different executors and add different qualifiers for each.
Use the first one for the first listener with one one parameter set and the second one for the other one with different parameter set. See example in this thread.
Another approach in your case could be that you call the right executor by adding executor name in annotation where you are invoking it.
Example:
#Configuration
#EnableAsync
class ApplicationAsyncConfiguration {
#Bean(name = "maxThreadingExecutor")
public Executor maxThreadingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(5);
executor.setCorePoolSize(5);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
#Bean(name = "defaultExecutor")
public Executor defaultExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(2);
executor.setCorePoolSize(2);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
}
#Component
public class MessageJmsListener {
#Async("maxThreadingExecutor")
#EventListener
public void onApplicationEvent(CustomMessage messageJmsWrapper) {
log.info("Current thread: {}", Thread.currentThread().getName());
}
#Async("defaultExecutor")
#EventListener
public void onApplicationEventSecond(SecondCustomMessage messageJmsWrapper) {
log.info("Second Listener thread: {}", Thread.currentThread().getName());
}
}
See more in this answer, might be helpful.
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 have a Spring #Configuration class as follows:
#Configuration
public class ExecutorServiceConfiguration {
#Bean
public BlockingQueue<Runnable> queue() {
return ArrayBlockingQueue<>(1000);
}
#Bean
public ExecutorService executor(BlockingQueue<Runnable> queue) {
return ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue);
}
#PreDestroy
public void shutdownExecutor() {
// no executor instance
}
}
I would also like to specify a #PreDestroy method which shuts down the ExecutorService. However, the #PreDestroy method cannot have any arguments which is why I'm not able to pass the executor bean to this method in order to shut it. Specifying destroy method in #Bean(destroyMethod = "...") does not work either. It allows me to specify existing shutdown or shutdownNow, but not the custom method which I intend to use.
I know I could instantiate the queue and executor directly and not as Spring beans, but I'd rather do it this way.
I love defining classes inline:
#Bean(destroyMethod = "myCustomShutdown")
public ExecutorService executor(BlockingQueue<Runnable> queue) {
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue) {
public void myCustomShutdown() {
...
}
};
}
Use the ThreadPoolTaskExecutor which does all that by default.
#Configuration
public class ExecutorServiceConfiguration {
#Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor() {
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
return new ArrayBlockingQueue<>(queueCapacity);
}
};
taskExecutor.setCorePoolSize(1);
taskExecutor.setMaxPoolSize(1);
taskExecutor.setKeepAliveSeconds(0);
taskExecutor.setQueueCapacity(1000);
return taskExecutor;
}
}
This will configure the ThreadPoolExecutor and shutdown when the application stops.
If you don't need the ArrayBlockingQueue but can live with the default LinkedBlockingQueue and only need to specify the queue capacity you can remove the override createQueue method.
I have a Spring Boot application which is responsible for answering requests via REST. I also push metrics about my application call. Since this is a separate task and I have to response users immediately, I want to make that metrics publishing asynchronously. So I've used that:
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("MyApp-");
executor.initialize();
return executor;
However, this one uses SimpleAsyncTaskExecutor and does not reuse any threads.
1) How can I use ConcurrentTaskExecutor instead of SimpleAsyncTaskExecutor?
2) Which implementation could best fit for my needs?
To make your custom Executor work, make sure it's registered as a Bean, and reference it on the method annotated with #Async:
#Bean(name = "transcodingPoolTaskExecutor")
public Executor transcodingPoolTaskExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("transcoder-");
executor.initialize();
return executor;
}
And in the #Service which is holding the method:
#Async("transcodingPoolTaskExecutor")
public void transcodeVideo(UUID fileId) {
mediaFileService.transcodeVideo(fileId);
}
You can also redefine the default executor by creating a Configuration class which implements AsyncConfigurer. Something like this:
#EnableAsync
#Configuration
public class AsyncConfig implements AsyncConfigurer {
#Value("${async.executor.corePoolSize:20}")
private int corePoolSize;
#Value("${async.executor.maxPoolSize:100}")
private int maxPoolSize;
#Value("${async.executor.queueCapacity:50}")
private int queueCapacity;
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("Async Executor -");
executor.initialize();
return executor;
}
}
In this case you wouldn't need to specify the executor name in #Async annotation. Spring will take executor, which you defined in AsyncConfig.
It's very similar to the solution provided by yglodt, but in this case you don't need to specify the name of executor in every #Async annotation, so it removes potential misspelling in the executor name.