Introduction
In this tutorial we will learn how to add asynchronous execution of tasks in different threads. Asynchronous execution of methods will be useful to scale services. We can use @EnableAsync and @Async annotations to add asynchronous execution in spring based applications.Realtime time case studies of asynchronous tasks, in a real time for example while attaching more than one attachment into gmail compose action, all attachments are started to upload to gmail server parallel.
And as another example, at system resources usage level to use server resources effectively we can execute bunch of tasks by dividing into small tasks and multiple threads can be executed concurrently. But if threads are completely locked server resources then application execution will be blocked which leads to restarting of server and application. So we are going to use threadpool system to allocate or create a specific number of threads.
At user view asynchronous is to perform multiple tasks at a same is good enough , but at application server manager view asynchronous is the matter of proper utilisation of cpu time and idle resources of server effectively. To avoid block of the application ( which will happen generally if all resources are using by threads or if task queue size is too much more ) we are using ThreadPoolTaskExecutor which is similar to ExecutorService in java.
Steps to implement asynchronous tasks execution
We are going to use @EnableAsync and @Async annotations to enable asynchronous tasks executionAs a vert first step annotate a configuration class with @EnableAsync, so that spring container will try to find a public methods which annotated with @Async - As a second step create threadpool by customizing ThreadPoolTaskExecutor by setting up number of threads to be created in threadpool and queue size etc...
- As a third step create a public methods which annotated with @Async annotation, where return type can be CompletableFuture<T>
- Invoke CompletableFuture.allOf(method1, method2...).join(); whereever we want to execute tasks in asynchronous manner
Step1: Enable configuration class with @EnableAsync
@Configuration @EnableAsync public class AsynConfig { // body here }
Step2: Creating Executor bean by Customising ThreadPoolTaskExecutor
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @EnableAsync public class AsynConfig { // body here @Bean("asynExecutor") public TaskExecutor geTaskExecutor() { ThreadPoolTaskExecutor tpte = new ThreadPoolTaskExecutor(); tpte.setCorePoolSize(4); tpte.setMaxPoolSize(8); tpte.setQueueCapacity(200); tpte.setThreadNamePrefix("MyAsysnTask:-"); tpte.initialize(); return tpte; } }
Step3: creating @Async method
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.ResponseEntity; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import com.jai43.jsite.models.Tasks; @Service public class TasksServices { private static Logger log = LoggerFactory.getLogger(TasksServices.class); @Autowired(required = true) RestTemplate rt; @Bean public RestTemplate rt() { return new RestTemplate(); } List<Tasks> getTasksByUserID(int userID) { ResponseEntity<Tasks[]> retasks = rt .getForEntity("https://jsonplaceholder.typicode.com/users/" + userID + "/todos", Tasks[].class); List<Tasks> lt = Arrays.asList(retasks.getBody()); return lt; } @Async public CompletableFuture<List<Tasks>> getTaks(int userID) throws InterruptedException { // Start the clock long start = System.currentTimeMillis(); log.info("Employee:--" + userID + "Started at" + start); List<Tasks> lt = this.getTasksByUserID(userID); // delay for demo purpose Thread.sleep(1000L); long end = System.currentTimeMillis(); log.info("Employee:--" + userID + "end at" + end); return CompletableFuture.completedFuture(lt); } }In the above code you can notice that in line 35 we have used @Async with a public method of CompletableFuture return type, and in line 46 we have invoke completedFuture() method of CompletableFuture by passing TypeMatch ( List<Tasks> ) object as a parameter.
Step4: Executing tasks with threads by using ComppletableFuture
import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.jai43.jsite.models.Tasks; import com.jai43.jsite.services.TasksServices; @RestController @RequestMapping("/tasks") public class TasksController { long start = System.currentTimeMillis(); private static Logger log = LoggerFactory.getLogger(TasksController.class); @Autowired TasksServices ts; @GetMapping("/tlist/{uid1}/{uid2}") public List<Tasks> getTasksByUserID(@PathVariable(name = "uid1") int uid1, @PathVariable(name = "uid2") int uid2) throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); log.info("started at" + start); CompletableFuture<List<Tasks>> ct1 = ts.getTaks(uid1); CompletableFuture<List<Tasks>> ct2 = ts.getTaks(uid2); CompletableFuture.allOf(ct1, ct2).join(); List<Tasks> userTList1 = ct1.get(); List<Tasks> userTList2 = ct2.get(); List<Tasks> list = new ArrayList<>(); list.addAll(userTList1); list.addAll(userTList2); long end = System.currentTimeMillis(); log.info("End at at" + end); return list; } }In the above code you can see line numbers 35 and 36 where we invoked @Async annotated methods and in line 38 we have added two tasks to CompletableFuture object with join() method. This will let wait until completion of threadpool tasks to proceed further step
Output with Async enabling
Executing tasks with Async |
Output without Aysnc
Executing tasks without Async |
comment from author
ReplyDelete