Run tasks in background in Spring
Why background task?
There are tons of examples where we often need to run a task in background e.g. sending an email, sms or notification, writing some log in file etc. If a task is not required to maintain atomicity and can be executed after some delay, it's a good candidate to run in background.How to run tasks in background in Java?
In Java, the easiest way to run a task in background is to create a new thread and execute it. It's quite simple. Here is how to do this: new Thread(new Runnable(){
@Override
public void run(){
// your background task
}
}).start();
It's easy! But there are some maintenance problem with the above code. The major problem is - there is no way to control how many background tasks we want to run simultaneously. For example, if 1000 users registers in your application at a time and you create 1000 background threads to send them a confirmation email, maybe you'll be in trouble. So you need to control number of background threads to run at a time.
What's the solution? You are a rockstar programmer and you know how to solve this problem using a Queue or some other data structure. But who wants to reinvent the wheel when Java already contains it? The solution is ExecutorService!.
What is a ExecutorService?
From Java doc:An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks.
In simple words, Executor service is an interface that defines the API to manage your asynchronous tasks e.g. add new task, define queue size, define number of threads, shutdown the tasks etc. For a detailed explanation on ExecutorService API methods you should read the Java doc.
Implementation of ExecutorService?
So Java has provided you an interface that defines the methods required to manage background or asynchronous tasks. Fine! But what about the implementation? Do you need to implement the ExecutorService? Or there is some other solution?Java provides some implementation of ExecutorService. Two important implementations are ScheduledThreadPoolExecutor and ThreadPoolExecutor. If you are not using spring, these implementations may be required in your application.
As we are going to use Spring in our project, we'll look for implementations in spring. Spring provides another interface TaskExecutor which extends ExecutorService via Executor interface. Spring provides several implementations of TaskExecutor interface. From the documentation, here are the implementations:
- SimpleAsyncTaskExecutor: This implementation does not reuse any threads, rather it starts up a new thread for each invocation. However, it does support a concurrency limit which will block any invocations that are over the limit until a slot has been freed up. If you are looking for true pooling, see the discussions of SimpleThreadPoolTaskExecutor and ThreadPoolTaskExecutor below.
- SyncTaskExecutor: This implementation doesn’t execute invocations asynchronously. Instead, each invocation takes place in the calling thread. It is primarily used in situations where multi-threading isn’t necessary such as simple test cases.
- ConcurrentTaskExecutor: This implementation is an adapter for a java.util.concurrent.Executor object. There is an alternative, ThreadPoolTaskExecutor, that exposes the Executor configuration parameters as bean properties. It is rare to need to use the ConcurrentTaskExecutor, but if the ThreadPoolTaskExecutor isn’t flexible enough for your needs, the ConcurrentTaskExecutor is an alternative.
- SimpleThreadPoolTaskExecutor: This implementation is actually a subclass of Quartz’s SimpleThreadPool which listens to Spring’s lifecycle callbacks. This is typically used when you have a thread pool that may need to be shared by both Quartz and non-Quartz components.
- ThreadPoolTaskExecutor: This implementation is the most commonly used one. It exposes bean properties for configuring a java.util.concurrent.ThreadPoolExecutor and wraps it in a TaskExecutor. If you need to adapt to a different kind of java.util.concurrent.Executor, it is recommended that you use a ConcurrentTaskExecutor instead.
- WorkManagerTaskExecutor: You should use the implementation that meets your need most. I'm going to use ThreadPoolTaskExecutor because in most cases you are going to need this.
How to use the implementation in Spring application?
Simple! Just declare the implementation as a Bean and spring will inject the implementation in your class whenever you ask with @Autowire. Here is a sample implementation of spring configuration: @Bean
public TaskExecutor getTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setMaxPoolSize(5);
return threadPoolTaskExecutor;
}
Set core pool size and max pool size to your required values. It depends on the frequency and execution time of the background task that you are going to process with TaskExecutor.
How to add task to TaskExecutor?
We've already declared a bean with name taskExecutor. To use this, we've to inject the bean in our application bean e.g. controller, service etc. like the following: @Autowired
private TaskExecutor taskExecutor;
And here is how you can add a task to taskExecutor
taskExecutor.execute(new Runnable() {
@Override
public void run() {
// your background task here
}
});
Pretty easy, huh? From any of your application bean you'll be able to send a task to taskExecutor. The TaskExecutor will run the task in background using the implementation you provided. That's it!
How to access other bean from your background task?
As you are using spring, it's obvious that you are using autowired beans for api calls, db queries etc. For example you access your database using a repository bean, you've created the spring RestTemplate bean and inject it where you need to do a API call. That's very common. But as we're creating new instances of Runnable, we'll not be able to use bean injection inside this class. So following example is not going to work: taskExecutor.execute(new Runnable() {
@Autowired
private PersonDao personDao;
@Override
public void run() {
personDao.getById(152); // just a call to dao
// your background task here
}
}
);
You'll get a NullPointerException as spring will not inject beans to manually created classes.
So what's the solution?
Spring comes with an elegant solution for this problem - the @Async annotation.From the spring documentation:
The @Async annotation can be provided on a method so that invocation of that method will occur asynchronously. In other words, the caller will return immediately upon invocation and the actual execution of the method will occur in a task that has been submitted to a Spring TaskExecutor. In the simplest case, the annotation may be applied to a void-returning method.
@Async void doSomething() {
// this will be executed asynchronously
}
In order to use this annotation in your application, you've to add @EnableAsync to your configuration class. If spring finds this annotation, it'll search for @Async annotated methods and create the Runnable implementations itself. You'll be able to use bean injection inside these methods like any other bean method. So following code will run properly:
@Service
class MyService {
@Autowired
private PersonDao personDao;
@Async
void doSomething() {
// this will be executed asynchronously
personDao.getById(152); // just a call to dao
}
}
Which task executor implementation will be used by Async?
By default spring will use a SimpleAsyncTaskExecutor implementation to run task asynchronously. If you need to override this behavior, you've two options1. Declare bean name with Async annotation
2. Declare a bean for application wise
For the first, you need to declare a Executor bean in your config class (where you declared @EnableAsync) and assign it a name like the following:
@Bean(name = "threadPoolTaskExecutor")
public TaskExecutor getTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setMaxPoolSize(5);
return threadPoolTaskExecutor;
}
Now you need to tell the Async method that you want the threadPoolTaskExecutor bean like the following
@Async("threadPoolTaskExecutor")
void doSomething() {
// this will be executed asynchronously
}
If you want to declare the executor bean for the whole application, your config class should implement the AsyncConfigurer interface and the bean you'll return from the getAsyncExecutor() method will be used by @Async annotated methods. Sample code:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
}
So that's all for now. For more information please read the spring documentation. Also the Baeldung trutorial is quite helpful to understand the spring Async.
Thanks Rafiqunnabi Nayan!
ReplyDeleteHi,
ReplyDeleteWould using only @Async in a method work? Or is mandatory to use @EnableAsync.