Asynchronous call in Spring Boot

Usually we developed procedures are invoked synchronously, namely the progressive implementation of the program in accordance with the order of down line by line of code, every line of code has to wait for the finished line of code to begin execution. The asynchronous programming is not the limit, the calling code is no longer blocked. Therefore, in some situations, can improve efficiency through asynchronous programming to enhance the throughput of the interface. This section explains how to make asynchronous programming in Spring Boot in.

 

Enables asynchronous

Spring Boot a new program, version 2.1.0.RELEASE, and introducing spring-boot-starter-web -dependent, project structure shown below:

To turn on asynchronous support, first of all you have to add in the Spring Boot entry class @EnableAsync notes:

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

 

Then start writing asynchronous method.

In com.example.demo new path under the service package, and create TestService :

@Service
public class TestService {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  @Async
  public void asyncMethod() {
      sleep();
      logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
  }

  public void syncMethod() {
      sleep();
  }

  private void sleep() {
      try {
          TimeUnit.SECONDS.sleep(2);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}

 

Service above contains an asynchronous method asyncMethod (after opening asynchronous support, only need to add in the way @Async annotation is the asynchronous method) and a synchronization method syncMethod . sleep method used to make the current thread blocked for 2 seconds.

Followed com.example.demo new route the controller package, and then create TestController :

@RestController
public class TestController {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  @Autowired
  private TestService testService;

  @GetMapping("async")
  public void testAsync() {
      long start = System.currentTimeMillis();
      logger.info("异步方法开始");

      testService.asyncMethod();

      logger.info("异步方法结束");
      long end = System.currentTimeMillis();
      logger.info("总耗时:{} ms", end - start);
  }

  @GetMapping("sync")
  public void testSync() {
      long start = System.currentTimeMillis();
      logger.info ( "Start Synchronization Method");       ; testService.syncMethod ()       ( "End Synchronization Method") logger.info;       Long End = System.currentTimeMillis ();       logger.info ( "Total time: {} MS ", End - Start);   } }







 

Start the project, visit http: // localhost: 8080 / sync request, the console output is as follows:

Can see the default procedure are synchronized, since sleep methodological reasons obstruction, TESTSYNC method performs more than 2 seconds.

Access HTTP: // localhost: 8080 / the async , the console output is as follows:

See testAsync time consuming very little, because of the asynchronous, the program has not been sleep blocking method, which is the benefits of asynchronous calls. While the internal asynchronous method will start a new thread to execute, a thread here named task - 1.

Asynchronous thread pool at the default configuration so that the thread can not be reused, each asynchronous method invocation will create a new thread, we can define your own asynchronous thread pool is optimized.

Custom asynchronous thread pool

In com.example.demo new under config package and then create AsyncPoolConfig configuration class:

@Configuration
public class AsyncPoolConfig {

  @Bean
  public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){
      ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
      executor.setCorePoolSize(20);
      executor.setMaxPoolSize(200);
      executor.setQueueCapacity(25);
      executor.setKeepAliveSeconds(200);
      executor.setThreadNamePrefix("asyncThread");
      executor.setWaitForTasksToCompleteOnShutdown(true);
      executor.setAwaitTerminationSeconds(60);

      executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

      executor.initialize();
      return executor;
  }
}

Above us ThreadPoolTaskExecutor some ways a custom thread pool, the meaning of these methods are as follows:

  • corePoolSize : number of core thread pool threads, the default value is 1 (this is the default case where the asynchronous thread pool threads arranged so that reason can not be reused).

  • maxPoolSize : The maximum number of threads in the thread pool maintenance, only when the core threads have been exhausted and after the buffer queue is full, will begin to apply more than the number of threads please kernel threads, the default value Integer.MAX_VALUE .

  • queueCapacity : buffer queue.

  • keepAliveSeconds : thread idle time exceeded the number of kernel threads outside the maximum survival time of 60 seconds by default.

  • threadNamePrefix : thread name prefix.

  • waitForTasksToCompleteOnShutdown : whether to wait for all the threads in the thread pool is finished before the close, the default value is false.

  • awaitTerminationSeconds : waitForTasksToCompleteOnShutdown long wait, the default value is 0, i.e., no waiting.

  • RejectedExecutionHandler : When processing strategies (denial task) when no thread can be used, the default policy is abortPolicy , contains the following four strategies:

    1. CallerRunsPolicy : processing for rejected tasks that runs the rejected task directly in the calling thread of the execute method; if the executor is closed, the task is discarded.

    2. abortPolicy : direct throw java.util.concurrent.RejectedExecutionException exception.

    3. discardOldestPolicy : When the number of threads in the pool equal to the maximum number of threads, the last to abandon the task to be performed in the thread pool, and implementation of new incoming tasks.

    4. discardPolicy : When the number of threads in the pool equal to the maximum number of threads, without any action.

To use this thread pool, just in @Async specify the thread pool to comment on Bean name:

@Service
public class TestService {
  ......

  @Async("asyncThreadPoolTaskExecutor")
  public void asyncMethod() {
      ......
  }
  ......
}

Restart the project, once again visit HTTP: // localhost: 8080 / the async , console output into the next:

Handling asynchronous callbacks

If the asynchronous method has a return value, the need to use the Future to receive callbacks value. We modified TestService of asyncMethod method to add Returns:

@Async("asyncThreadPoolTaskExecutor")
public Future<String> asyncMethod() {
  sleep();
  logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
  return new AsyncResult<>("hello async");
}

 

Specify generic type of return value, the AsyncResult is achieved Spring Future implementation class:

Then the transformation TestController of testAsync method:

@GetMapping("async")
public String testAsync() throws Exception {
  long start = System.currentTimeMillis();
  logger.info("异步方法开始");

  Future<String> stringFuture = testService.asyncMethod();
  String result = stringFuture.get();
  logger.info("异步方法返回值:{}", result);
   
  logger.info("异步方法结束");

  long end = System.currentTimeMillis();
  logger.info("总耗时:{} ms", end - start);
  return stringFuture.get();
}

 

Future接口的get方法用于获取异步调用的返回值。

重启项目,访问 http://localhost:8080/async 控制台输出如下所示:

通过返回结果我们可以看出Futureget方法为阻塞方法,只有当异步方法返回内容了,程序才会继续往下执行。get还有一个get(long timeout, TimeUnit unit)重载方法,我们可以通过这个重载方法设置超时时间,即异步方法在设定时间内没有返回值的话,直接抛出java.util.concurrent.TimeoutException异常。

比如设置超时时间为60秒:

String result = stringFuture.get(60, TimeUnit.SECONDS);

Guess you like

Origin www.cnblogs.com/7788IT/p/11626867.html