Spring Boot asynchronous thread


General background management system has export capabilities of the report, for the report export large amounts of data, usually more time-consuming, such as administrator clicks a button to export, often have to wait a long time until we can successfully export the report to the next step obviously such a synchronized manner has failed to meet the demand. The actual development is now commonly used way is to use JMS message queue ways to send messages to other systems for export, or turn on the asynchronous thread in the project to complete the export time-consuming work. This paper will report export scenarios to explain how some of the Spring Boot enables asynchronous thread.

Custom thread pool available and enables asynchronous
Spring present an interface AsyncConfigurer interface that is used to configure the asynchronous interface thread pool, it has two methods, getAsyncExecutor and getAsyncUncaughtExceptionHandler, the first method is to obtain a thread pool, the second the method is used to handle an abnormality occurs in the asynchronous thread. Its source is as follows:

package org.springframework.scheduling.annotation;

import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.lang.Nullable;

public interface AsyncConfigurer {

    // 获取线程池
    @Nullable
    default Executor getAsyncExecutor() {
        return null;
    }

    // 异步异常处理器
    @Nullable
    default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}


Here's interface provides are empty achieve, so I want to open asynchronous thread mechanism, then we need to manually implement this interface, the class will implement the interface labeled Spring configuration class, then opened the Spring asynchronous is available, Spring will be obtained by a thread getAsyncExecutor be used to perform an asynchronous operation, of course, also need to open the entire binding asynchronous two annotations, one @EnableAsync, the other is @Async, is marked in a first configuration class in that tells Spring is available asynchronously, the second comment is usually marked in one method, when calling this method, you will get a new thread from the thread pool to execute it.
Now let's define the thread pool and asynchronous open is available, here to write a profile class AsyncConfig to achieve AsyncConfigurer, code as follows:

package cn.itlemon.springboot.async.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:28
 */
@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        // 自定义线程池
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数
        taskExecutor.setCorePoolSize(10);
        // 最大线程数
        taskExecutor.setMaxPoolSize(30);
        // 线程队列最大线程数
        taskExecutor.setQueueCapacity(2000);
        // 初始化线程池
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> {
            log.error("Error Occurs in async method:{}", ex.getMessage());
        };
    }
}



The first method we define a thread pool and set some basic parameters, such as the core number of threads, maximum number of threads, thread queue and the maximum number of threads, the second method is asynchronous exception handling thread, which is a exception handler, returns the class object AsyncUncaughtExceptionHandler interface to achieve, since the interface is a function AsyncUncaughtExceptionHandler (abstract only one interface method, denoted generally used @FunctionalInterface annotation interface), so Lambda expressions used herein abbreviated to its implementation class objects, asynchronous exception handling is recorded here about the log, and did not do other logical operation, if not familiar with Lambda expressions, you can directly use an anonymous inner class to create an implementation class object AsyncUncaughtExceptionHandler, as follows:

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

    return new AsyncUncaughtExceptionHandler() {
        @Override
        public void handleUncaughtException(Throwable ex, Method method, Object... params) {
            log.error("Error Occurs in async method:{}", ex.getMessage());
        }
    };
}


One thing to note is that we added @EnableAsync notes in the above configuration class, then register the configuration classes in Spring is the time of the Spring Bean, it will open asynchronous mechanisms available.

Test asynchronous mechanism available
to write a Service layer interface for generating reports show:

package cn.itlemon.springboot.async.service;

import java.util.concurrent.Future;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:32
 */
public interface AsyncService {

    /**
     * 模拟生成报表的异步方法
     */
    void generateReport();

}


Its implementation class is:

package cn.itlemon.springboot.async.service.impl;

import cn.itlemon.springboot.async.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.concurrent.Future;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:33
 */
@Service
public class AsyncServiceImpl implements AsyncService {

    @Override
    @Async
    public void generateReport() {
        // 模拟异步生成报表代码,这里设置为打印
        System.out.println("报表线程名称:【" + Thread.currentThread().getName() + "】");
    }
    
}


It is assumed that the work has been exporting a report, so use print statements to a simple simulation, and marked @Async annotation method, then when calling the method, Spring will acquire a new thread to execute this method, so here to print out the name of the thread execution of the current method. We write a controller, as follows:

package cn.itlemon.springboot.async.controller;

import cn.itlemon.springboot.async.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:36
 */
@RestController
@RequestMapping("/async")
@Slf4j
public class AsyncController {

    private final AsyncService asyncService;

    @Autowired
    public AsyncController(AsyncService asyncService) {
        this.asyncService = asyncService;
    }

    @GetMapping("/page")
    public String asyncPage() {
        System.out.println("当前请求线程名称为:【" + Thread.currentThread().getName() + "】");
        // 异步调用
        asyncService.generateReport();
        // 返回结果
        return "async";
    }
    
}


We also print the current thread in the current Controller process, run the project, access to the specified URL, you can compare when calling generateReport method whether the new thread is enabled. We start Spring Boot application, in the browser address bar: http: // localhost: 8080 / async / page, the result is printed in the console:

The current request thread name: [http-nio-8080-exec- 1 ]
Reports Thread Name: [ThreadPoolTaskExecutor-1]

Obviously, this is not the same thread, that we open asynchronous thread successfully.

Exception handling asynchronous thread
typically processed in an asynchronous thread exception Spring divided into two categories, one is an asynchronous method does not return a value, and the other is an asynchronous method returns a value.

The first method does not return a value
for the first class no return value, we have been in AsyncConfig configuration class configuration, namely to achieve getAsyncUncaughtExceptionHandler method, that is, when the code asynchronous thread exception occurs, we will call this method to exception handling, for testing, our method of generateReport AsyncServiceImpl manually add a line System.out.println (1/0) ;, causing it to the zero exception, the code is as follows:

@Override
@Async
public void generateReport() {
    // 模拟异步生成报表代码,这里设置为打印
    System.out.println("报表线程名称:【" + Thread.currentThread().getName() + "】");
    System.out.println(1 / 0);
}



When starting Spring Boot application again in the browser address bar: http: // localhost: 8080 / async / page, then the exception occurs in an asynchronous process, because it is an exception occurs in different threads, so it does not Effects after execution of the main thread, and the exception occurs, the method getAsyncUncaughtExceptionHandler configured, then the exception will be processed, the processing mode is to use log recorded:

10 2018-10-31: 57 is: ERROR 2391 --- 09.952 [-lTaskExecutor. 1] cispringboot.async.config.AsyncConfig: Error the Occurs in the async Method: / by ZERO
. 1
the second class has a method return value
for the second case that the asynchronous method will return a value, then how do we get to the return value of the asynchronous thread processing, the usual method is to return value of asynchronous method of using the interface Future, ListenableFuture or class AsyncResult packaging, will soon return value as a generic passing to said interface or class. Here we briefly examine the methods they used in the source code.

Future interfaces:

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);
    
    boolean isCancelled();
    
    boolean isDone();
    
    V get() throws InterruptedException, ExecutionException;
    
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}



The method of analysis:

cancel method to cancel the task, if the task successfully cancel returns true if it returns false to cancel the mission fails. MayInterruptIfRunning parameter indicates whether to allow cancel the task being performed but has not been completed, if set true, the task can be canceled in the process of being implemented. If the task has been completed, regardless mayInterruptIfRunning is true or false, this method certainly return false, that is, if the cancellation has been completed task returns false; if the task being performed, if mayInterruptIfRunning set to true, returns true if mayInterruptIfRunning set to false , false is returned; if the task has not been executed, regardless mayInterruptIfRunning is true or false, certainly return true.
isCancelled method indicates whether the task was canceled successfully, if success is canceled before the task is completed normally, it returns true.
isDone method indicates whether the task has been completed, if the task is completed, return to true;
GET method is used to obtain the results, this method will produce obstruction, will wait until the task is finished before returning;
GET (Long timeout, TimeUnit Unit) used to get the results, if within the specified time, not to get a result, the direct return null.
ListenableFuture Interface:

public interface ListenableFuture<T> extends Future<T> {

    void addCallback(ListenableFutureCallback<? super T> callback);

    void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback);

    default CompletableFuture<T> completable() {
        CompletableFuture<T> completable = new DelegatingCompletableFuture<>(this);
        addCallback(completable::complete, completable::completeExceptionally);
        return completable;
    }
}


ListenableFuture inherited the Future interfaces, it also adds three additional methods, mainly used to add asynchronous callbacks site can be used to handle exceptions and get the return value of asynchronous method. AsyncResult ListenableFuture class implements an interface, it also implements all the methods. Next, we will introduce how to capture the return value of asynchronous processing and exception handling.

Future use of the interface

We add a AsyncService interface method: ReturnMessage (), and uses the interface to Future packaging, as follows:

/ **
 * Method asynchronous callback message
 *
 * @return string
 * /
Future <String> ReturnMessage ();

implementation code class is as follows:

@Override
@Async
public Future<String> returnMessage() {
    System.out.println(Thread.currentThread().getName());
    String message = "Async Method Result";
    return new AsyncResult<>(message);
}


Then Controller layer, can be acquired to achieve the object class of the Future, as follows:

@GetMapping("/page1")
public String asyncPage1() {
    try {
        System.out.println(Thread.currentThread().getName());
        Future<String> result = asyncService.returnMessage();
        System.out.println(result.get());
    } catch (ExecutionException | InterruptedException e) {
        log.error("发生了异常:{}", e.getMessage());
    }
    return "async";
}


Here asynchronous been try ... catch exception handling, also used the Future of the get method to get the asynchronous method's return value, but this acquisition mode the return value will block the current thread, that is to say, after calling the get method, asynchronous thread waits for the next line of code before execution is completed.

Use ListenableFuture Interface

We add a AsyncService interface method: returnMsg (), and uses the interface to ListenableFuture packaging, as follows:

/**
 * 异步回调消息方法
 *
 * @return 字符串
 */
ListenableFuture<String> returnMsg();


Implementation class code as follows:

@Override
@Async
public ListenableFuture<String> returnMsg() {
    System.out.println(Thread.currentThread().getName());
    String message = "Async Method Result";
    return new AsyncResult<>(message);
}


Then Controller layer, can be achieved to obtain the ListenableFuture class object, as follows:

@GetMapping("/page2")
public String asyncPage2() {
    System.out.println(Thread.currentThread().getName());
    ListenableFuture<String> result = asyncService.returnMsg();
    result.addCallback(new SuccessCallback<String>() {
        @Override
        public void onSuccess(String result) {
            System.out.println("返回的结果是:" + result);
        }
    }, new FailureCallback() {
        @Override
        public void onFailure(Throwable ex) {
            log.error("发生了异常:{}", ex.getMessage());
        }
    });
    return "async";
}


As it can be seen from the above code, add the two callback returned results, respectively, to achieve the object class and the asynchronous processing of asynchronous processing of callback interfaces SuccessCallback failure to achieve successful callback class object interface FailureCallback abnormality occurs. ListenableFuture Interface is an extension of the Future interface, supports callback, effectively avoid thread blocking issue, that is, it will monitor the implementation of the Future interface, once completed, it will call onSuccess method of treatment success, in the event of abnormal , onFailure method is called exception handling. In comparison, more recommended ListenableFuture to have a return value of asynchronous processing. For Java1.8, in fact, more or guava recommended CompletableFuture of ListenableFuture, interested students can conduct in-depth research, they will be more powerful ability to handle asynchronous
 

Published 48 original articles · won praise 26 · views 70000 +

Guess you like

Origin blog.csdn.net/qq_38316721/article/details/104883281