Spring Boot Async Task Executor

By Dhiraj Ray, 01 October,2017   6K
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);
}
 Other Interesting Posts
Spring Boot Hibernate 5 Example
Spring Hibernate Integration Example
Spring Boot Actuator Rest Endpoints Example
Spring 5 Features and Enhancements
Spring Boot Thymeleaf Example
Securing REST API with Spring Boot Security Basic Authentication
Spring Boot Security Password Encoding using Bcrypt Encoder
Spring Security with Spring MVC Example Using Spring Boot
Websocket spring Boot Integration Without STOMP with complete JavaConfig

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

Further Reading on Spring Boot

1. Spring Boot Multiple Database Configuration

2. Spring Boot Hibernate 5 Example

3. Spring Boot H2 Database Example

4. Spring Data Jpa Example

5. Spring Boot Jms Activemq Example

If You Appreciate What We Do Here On Devglan, You Should Consider:

References

Spring Async Docs

TaskExecutor with Annotation

ThreadPool Executor