SEARCH BY

All

  • All
  • Java8
  • Spring

How to do asynchronous execution support in Spring Boot applications

1 comment

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 execution
  1. As 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
  2. As a second step create threadpool by customizing ThreadPoolTaskExecutor by setting up number of threads to be created in threadpool and queue size etc...
  3. As a third step create a public methods which annotated with @Async annotation, where return type can be CompletableFuture<T>
  4. 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
Executing tasks with Async
In the above screen you can see two threads ( one is for getting tasks list of employee 1 and second thread is for getting tasks list of employee 3) started at same time, because our core threadpool size is 4 which we set in the TaskExecutor bean configuration in the above code you can see that.

Output without Aysnc

Executing tasks without Async
Executing tasks without Async
In the above screen ( which is without @Async ) you can notice that Getting employee 3 details started after completing an action of getting employee 1 details, that means tasks are executed with sequential manner only.
jaya
I love software designing, coding, writing and teaching...

Share this article

Related Posts

1 comment

Post a Comment

Place your code in <em> </em> tags to send in comments | Do not add hyper links in commnet

Close

Subscribe our newsletter for the latest articles directly into your email inbox