Spring Boot Async Task Executor

By Dhiraj Ray, 01 October,2017  
773
spring-boot-2.0

In this post we will be discussing about spring boot asynchronous execution support using async task executor feature to execute task in a different thread. We will take a look into configuring SimpleAsyncTaskExecutor, ConcurrentTaskExecutor, ThreadPoolExecutor in a spring project. Apart from this, we will be also looking into how actual method return type can be wrapped in a Future object while dealing with async behaviour in spring.So let us get started with spring boot async task executor.

Async Configuration in Spring

To enable async behaviour in Spring, annotate your configuration class with @EnableAsync.

@EnableAsync @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

@EnableAsync: - It detects @Async annotation.

mode - The mode() attribute controls how advice is applied. By default its value is AdviceMode.PROXY. Note that if the mode() is set to AdviceMode.ASPECTJ, then the value of the proxyTargetClass() attribute will be ignored. Note also that in this case the spring-aspects module JAR must be present on the classpath.

proxyTargetClass - It defines the type of proxy that would be used from CGLIB or JDK.By default its CGLIB.

Using @Async Annotation

This annotation is used on the method level for those method which you want it execution to be in a seperate thread.This annotation works as expected if a public method is annotated with this annotation.

Also, the method needs to be called from a different class so that it can be proxied else the proxy will be bypassed.

Following is an example of an @Async annotated method. It does not return any value.

@Override @Async public void createUserWithDefaultExecutor(){ //SimpleAsyncTaskExecutor System.out.println("Currently Executing thread name - " + Thread.currentThread().getName()); System.out.println("User created with default executor"); }

By default, Spring will be searching for an associated thread pool definition: either a unique TaskExecutor bean in the context, or an Executor bean named "taskExecutor" otherwise. If neither of the two is resolvable, a SimpleAsyncTaskExecutor will be used to process async method invocations

Using @Async Annotation with Method Return Type

The actual return type of a method can be wrapped in a Future object.

@Override @Async public Future createAndReturnUser() { System.out.println("Currently Executing thread name - " + Thread.currentThread().getName()); try { User user = new User(); user.setFirstName("John"); user.setLastName("Doe"); user.setGender("Male"); Thread.sleep(5000); return new AsyncResult(user); } catch (InterruptedException e) { System.out.println(e.getMessage()); } return null; }

Following is a test method for this.

@Test public void createAndReturnUserTest() throws ExecutionException, InterruptedException { System.out.println("Current Thread in test class " + Thread.currentThread().getName()); long startTime = System.currentTimeMillis(); Future futureUser = userService.createAndReturnUser(); futureUser.get(); assertTrue((System.currentTimeMillis() - startTime) >= 5000); }

Defining ThreadPoolTaskExecutor and ConcurrentTaskExecutor at Method Level

By default spring uses SimpleAsyncTaskExecutor to run methods annotated with @Async. We can also define our custom executor bean as follow and use it at method level.

ThreadPoolTaskExecutor
@Bean(name = "threadPoolExecutor") public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(7); executor.setMaxPoolSize(42); executor.setQueueCapacity(11); executor.setThreadNamePrefix("threadPoolExecutor-"); executor.initialize(); return executor; }
ConcurrentTaskExecutor
@Bean(name = "ConcurrentTaskExecutor") public TaskExecutor taskExecutor2 () { return new ConcurrentTaskExecutor( Executors.newFixedThreadPool(3)); }

These beans can be used at method level in following ways

@Override @Async("threadPoolExecutor") public void createUserWithThreadPoolExecutor(){ System.out.println("Currently Executing thread name - " + Thread.currentThread().getName()); System.out.println("User created with thread pool executor"); } @Override @Async("ConcurrentTaskExecutor") public void createUserWithConcurrentExecutor(){ System.out.println("Currently Executing thread name - " + Thread.currentThread().getName()); System.out.println("User created with concurrent task executor"); }

SimpleAsyncTaskExecutor does make sense in cases, if you want to execute some long-time-executing tasks, e.g. if you want to compress log files at the end of a day. In other cases, if you want to execute a short-time-executing task every n seconds or minutes, you should use the ThreadPoolTaskExecutor, because of reusing of system resources.

Implementing Executor at Application Level

To implement executor at application level, we require to implement AsyncConfigurer and override folowing methods.

@Configuration public class AsyncConfig implements AsyncConfigurer { @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 new AsyncExceptionHandler(); } }

Following is the exception handler.

AsyncExceptionHandler.class
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... obj) { System.out.println("Exception Cause - " + throwable.getMessage()); System.out.println("Method name - " + method.getName()); for (Object param : obj) { System.out.println("Parameter value - " + param); } } }

Conclusion

I hope this article served you that you were looking for. If you have anything that you want to add or share then please share it below in the comment section.

Download the source

References

Spring Async Docs

TaskExecutor with Annotation

ThreadPool Executor

Suggest more topics in suggestion section or write your own article and share with your colleagues.

Is this page helpful to you? Please give us your feedback below. We would love to hear your thoughts on these articles, it will help us improve further our learning process.

Further Reading: