How do springboot @ Async annotations achieve method asynchrony

  • 2021-12-12 08:51:06
  • OfStack

Directory @ Async annotation how to implement method asynchronous 1. App class of springboot required annotation 2. service layer annotation 3. Call layer asynchronous annotation @ Async use and precautions step 1 turn on asynchronous the following shows the code implementation of configuring threads. Using @ Async leads to unsuccessful asynchronous

How does @ Async annotation achieve method asynchronism

When dealing with large quantities of data, the efficiency is very slow. So consider using multithreading under 1.

I just started a set of handwritten by myself, and used the thread pool to start a fixed number of threads to run batches. But later, the boss considered that the risk of handwriting was not easy to control, so he used spring method.

There is no detailed introduction here, only a simple demo, which can only be used and does not understand the principle:

1. Required annotations for App class of springboot


package com.xxx.xxx.xxx;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.boot.web.support.SpringBootServletInitializer;
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;
/**
 *  Class function description : Service producer startup class 
 * <p>
 * <strong></strong>
 * </p>
 *
 * @version
 * @author
 * @since 1.8
 */
@Configuration
@EnableAsync
public class Application extends SpringBootServletInitializer { 
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //  Set the number of core threads 
        executor.setCorePoolSize(5);
        //  Set the maximum number of threads 
        executor.setMaxPoolSize(60);
        //  Setting Queue Capacity 
        executor.setQueueCapacity(20);
        //  Set thread active time (seconds) 
        executor.setKeepAliveSeconds(60);
        //  Set the default thread name 
        executor.setThreadNamePrefix("what-");
        //  Setting a Deny Policy 
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //  Wait for all tasks to finish before closing the thread pool 
        executor.setWaitForTasksToCompleteOnShutdown(true);        
        return executor;
    }    
}

springboot App class, very simple, can use a lot of things.

2. Notes on service Layer


package com.xxx.xxx.service.impl; 
import java.util.concurrent.Future; 
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service; 
import com.xxx.xxx.service.XXXAsyncService ;
 
@Service
public class XXXAsyncServiceImpl implements XXXAsyncService { 
    @Async
    public Future<Long> rtn1() throws Exception {
        //do something
        // When there is a return value, you can return it string , long Something like that. 
        return new AsyncResult<>(1);
    } 
    @Async
    public void rtn2() throws Exception {
        //do something
        // This can have no return value .
    }
}

3. Call layer


package com.xxx.xxx.controller; 
import java.util.concurrent.Future; 
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; 
import com.xxx.xxx.service.XXXAsyncService;
 
@RestController
@RequestMapping(value="/xxx") 
public class XXXAsyncController { 
 @Autowired
 private XXXAsyncService xxxAsyncService; 
 /**
  *  The asynchronous method is called here 
  */
    @RequestMapping(value = "/xxx")
 public void dodo() throws Exception {     
  int threads = 10;//10 Threads 
        List<Future<Long>> list = new ArrayList<>();
        for(int i = 0;i < threads; i++){
         // Here, the asynchronous method is called in a loop. 
      // If there is a large amount of data, you can slice the data here, and then call it cyclically to process the data in batches. Efficiency lever. 
   list .add(xxxAsyncService.rtn1());
        }
        long count = 0;
        for(Future<Long> l : tsfCountList) {
         // When an asynchronous call needs a return value, you can put all the return values into the list Collection, and then you can unify 1 Handle.   Here's get() Is blocked, because the asynchronous method returns and continues execution. 
         count += l.get();
        }
        System.out.println(" Number of calls: " + count) ; 
 }
}

These codes are all handwritten and recorded. When you use them later, you can forget them and find them troublesome. .

Use and considerations of asynchronous annotation @ Async

Step 1 Turn on asynchronous


@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }

By default, @ EnableAsync detects the @ Async annotation of Spring and EJB 3.1 javax. EJB. Asynchronous; This option can also be used to detect other user-defined annotation types. (You can also add @ EnableAsync annotation directly to the startup class of SpringBoot.)

In Spring, the specified method is annotated with @ Async, which executes asynchronously when called. If no thread pool is specified in the @ Async annotation, the default thread pool is used. The default thread pool is SimpleAsyncTaskExecutor.

The thread pool does not reuse threads, and for every new task submitted, the thread pool creates a new thread instance to execute the task. The following is the related code:


protected void doExecute(Runnable task) {
    Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
    thread.start();
}

If you want to specify a thread pool, you can specify the Bean Name of the thread pool you want to use in the value parameter in the @ Async annotation. Another method is to be a configuration class that implements the AsyncConfigurer interface or inherits its default adapter class AsyncConfigurerSupport, so that the @ Async annotated method uses the specified custom thread pool.

If you use @ Async annotation, you use the default thread pool of springBoot, but generally we will customize the thread pool (because it is more flexible), and the configuration methods are as follows:

How to configure using xml file Configuration using the Java code in conjunction with @ Configuration (recommended)

The code implementation of the configuration thread is shown below


package com.deppon.ptos.load.config; 
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
 
/**
 * @Description:  Asynchronous thread management 
 * @Author:   LYH
 * @CreateDate: 2019/6/27 8:54
 * @Version: 1.0
 * @JDK: 1.8
 */
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {  
    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;
 
    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        // Configure the number of core threads 
        executor.setCorePoolSize(corePoolSize);
        // Configure the maximum number of threads 
        executor.setMaxPoolSize(maxPoolSize);
        // Configure queue size 
        executor.setQueueCapacity(queueCapacity);
        // Configure the name prefix of threads in the thread pool 
        executor.setThreadNamePrefix(namePrefix);
 
        // rejection-policy : When pool Has reached max size How to deal with new tasks when 
        // CALLER_RUNS The task is not executed in a new thread, but is executed by the caller's thread 
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // Perform initialization 
        executor.initialize();
        return executor;
    }
}

package com.deppon.ptos.load.config;  
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture; 
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
 
/**
 * @Description:  Print the execution of asynchronous threads     Use Callbale Future  To return information about the thread 
 * @Author: 633805  LYH
 * @CreateDate: 2019/6/27 8:59
 * @Version: 1.0
 * @JDK: 1.8
 */
@Component
@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {  
    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor(); 
        if (null == threadPoolExecutor) {
            return;
        }
 
        log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }
 
    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }
 
    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }
 
    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }
 
    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }
 
    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }
 
    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }
}

Use:


@Async("asyncServiceExecutor")

At this step, asynchronism is turned on.

Let's mainly talk about 1. Say the wrong ones

Using @ Async causes asynchronous unsuccessful conditions

@ Async is disabled in the following ways

Asynchronous methods are decorated with static The asynchronous class does not use the @ Component annotation (or other annotations) so that spring cannot scan the asynchronous class An asynchronous method cannot be in the same class as the called asynchronous method Class requires automatic injection with annotations such as @ Autowired or @ Resource, and you cannot manually new objects yourself If you use the SpringBoot framework, you must add the @ EnableAsync annotation to the startup class

Related articles: