Spring provides @async to implement asynchronous calls to methods. That is, when a method labeled with the @async identifier is called, the calling thread does not wait for the method to finish executing before continuing to perform subsequent operations, but starts an independent thread for the method to execute.

This asynchronous execution is typically used to handle data processing in the interface that does not need to be returned to the user. Such as:

When registering, only the user information is returned to the user, and the saving of the information can be performed asynchronously.

In asynchronous methods, you develop your own code blocks to define specific pairs of business logic, then hand them over to the Spring container to manage and start a new thread to execute the corresponding method. Special attention should be paid to the configuration of the thread pool and the handling of exceptions in the task when using asynchronous methods. The following is a brief description of this feature.

Introduction to the

Enable functionality annotation: @enableAsync enables Spring’s asynchronous method functionality by adding this annotation to Spring’s configuration class.

Asynchronous methods identify the annotation Async, which is defined as:

// for type and method.@target ({ElementType. Method, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Async { String value() default ""; }Copy the code

When applied to a class, this annotation indicates that all methods in the class are asynchronous. The value attribute in this annotation can be used to specify the thread pool to execute this asynchronous method. The specific determination method of thread pool is analyzed below.

Spring thread pool selection and custom configuration thread pool

Instead of manually creating threads ourselves in projects, tasks or asynchronous methods are executed through a unified thread pool to avoid resource exhaustion caused by custom threads in multi-person teams. Before customizing thread pools, you should first understand how Spring selects thread pools when executing asynchronous tasks or methods.

@async Selection order for thread pools

Spring first searches the Spring context for a bean of type TaskExecutor or named “TaskExecutor” when it can be found and submits the task to the thread pool for execution. When the above thread pool does not exist, Spring manually creates a SimpleAsyncTaskExecutor to perform asynchronous tasks.

In addition, when the @async annotation specifies a value, Spring will use the specified thread pool to execute. For example:

@async (value = “asyncTaskThreadPool”) At this point Spring will look for a bean named asyncTaskThreadPool in the context and execute the asynchronous task. If not, it will throw an exception.

Custom thread pools and exception handling

Now that we know @Async’s thread pool of choice, we need to customize the thread pool. There are three ways to customize Async thread pools.

Method 1: Override @async to get the thread pool.

To configure the @Async thread pool, either inherit the AsyncConfigurerSupport class or implement the AsyncConfigurer interface and rewrite the getAsyncExecutor method as follows:

@Configuration @EnableAsync public class ThreadPoolBeanFactory extends AsyncConfigurerSupport{ @Override public Executor  getAsyncExecutor() { ThreadPoolTaskExecutor asyncTaskThreadPool = new ThreadPoolTaskExecutor(); asyncTaskThreadPool.setCorePoolSize(100); asyncTaskThreadPool.setMaxPoolSize(200); asyncTaskThreadPool.setQueueCapacity(11); asyncTaskThreadPool.setThreadFactory(new ThreadFactory() { private final AtomicLong index = new AtomicLong(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "Async-override-task-pool-thread-" + index.getAndIncrement()); }}); asyncTaskThreadPool.initialize(); return asyncTaskThreadPool; } @ Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler () {/ / the return value is void of asynchronous method can't pass exceptions to this, When an exception occurs in a method, only a log is printed. Override this method to customize the exception handling method return null. }}Copy the code

The disadvantage of this defined approach is that there is no explicit thread pool bean object defined.

Method two: Customize thread pool beans of the appropriate type.

The second method is based on Spring @async’s thread pool selection principle and defines a TaskExecutor bean as follows:

@Bean public TaskExecutor asyncTaskThreadPool() { ThreadPoolTaskExecutor asyncTaskThreadPool = new ThreadPoolTaskExecutor(); asyncTaskThreadPool.setCorePoolSize(100); asyncTaskThreadPool.setMaxPoolSize(200); asyncTaskThreadPool.setThreadFactory(new ThreadFactory() { private final AtomicLong index = new AtomicLong(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "Async-task-pool-thread-" + index.getAndIncrement()); }}); // asyncTaskThreadPool.initialize(); // Return asyncTaskThreadPool is automatically called when the container is loaded. }Copy the code

test

Use thread pools defined in both ways. The code is as follows:

@Async
public void test(){
    System.out.println(Thread.currentThread().getName());

}
Copy the code

Spring automatically executes this method using the thread pool defined above. The output of the two configurations is as follows:

Async-task-override-pool-thread-1
Async-task-pool-thread-1
Copy the code

Method three executes the thread pool name in the Async annotation

Asynchronous tasks are defined as follows:

@Async(value = "asyncTaskThreadPool")
public void asyncTask2() {
    LOGGER.info("AsyncTask2 start.");
    LOGGER.info(Thread.currentThread().getName());
    LOGGER.info("AsyncTask2 finished.");
}
Copy the code

At this point Spring looks for a thread pool in the context named asyncTaskThreadPool to perform this task.

@async returns the operation result

An asynchronous task can receive a return value by defining the return type as Future, as follows:

@Async
public Future<String> asyncTaskWithResult() {
    LOGGER.info("AsyncTaskWithResult start.");
    try {
        Thread.sleep(1000 * 10);
    } catch (Exception e) {
        return new AsyncResult<>("error" + e.getMessage());
    }
    LOGGER.info("AsyncTaskWithResult finished.");

    return new AsyncResult<>("success");
}
Copy the code

The unit test code is as follows:

@RunWith(SpringRunner.class)
@SpringBootTest
public class AsyncApplicationTests {
    @Autowired
    private AsyncTaskService asyncTaskService;

    @Test
    public void asyncTest() throws Exception{
        Future<String> future = asyncTaskService.asyncTaskWithResult();

        while (!future.isDone()) {
            System.out.println("Wait asyncTaskWithResult.");
            Thread.sleep(1000);
        }
        System.out.println("asyncTaskWithResult result is:" + future.get());
        System.out.println("asyncTask finished.");

    }
}
Copy the code

The output is as follows:

AsyncTaskWithResult start.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
AsyncTaskWithResult finished.
asyncTaskWithResult result is:success
asyncTask finished.
Copy the code

Pay attention and don’t get lost

The article continues to update every week, you can wechat search “ten minutes to learn programming” the first time to read and urge more, if this article is written well, feel something ~ for praise 👍 for attention ❤️ for share ❤️ everyone’s support and recognition, is the biggest power of my creation, we see the next article!