Spring boot notes @Async

From the beginning Spring3 provides @Async notes, the notes may be marked on the method to call the method asynchronously. The caller will be returned when called immediately, the actual implementation of the method will be presented to the Spring TaskExecutor task, performed by a designated thread pool thread.

1. TaskExecutor

Spring asynchronous thread pool interface class, and its essence is java.util.concurrent.Executor

Spring has been achieved abnormal thread pool:
1. SimpleAsyncTaskExecutor: Not really thread pool, this class does not reuse threads, each call will create a new thread.
2. SyncTaskExecutor: This class does not implement an asynchronous call, just a synchronous operation. Applies only to multi-threading does not need to place
3. ConcurrentTaskExecutor: Executor of adaptation classes, not recommended. If ThreadPoolTaskExecutor not meet the requirement, with only consider using this class
4. SimpleThreadPoolTaskExecutor: Quartz is a SimpleThreadPool class. Thread pool while being quartz and non-quartz use, only need to use this type
5. ThreadPoolTaskExecutor: most commonly used, is recommended. Its essence is the packaging java.util.concurrent.ThreadPoolExecutor

2. @EnableAsync @Async

(1) springboot start classes, @EnableAsync comment open an asynchronous call

(2) spring asynchronous tasks defined for @Async

There are three methods of asynchronous
1. The most simple asynchronous call, the return value is void, no return value based @Async call, using the class directly, using a method (method using recommended) on the annotate. If you need to throw an exception, you require a new manual exception is thrown.
2. asynchronous asynchronous method calls with parameters can pass parameters
3. abnormal call returns Future, will not be processed AsyncUncaughtExceptionHandler, we need to catch the exception in the method and process or catch the exception when calling the caller is processed in Futrue.get

 3. @Async use the default thread pool

spring applies a default thread pool, refers @Async comment in use, the name of the thread pool is not specified. View source, @ Async default thread pool SimpleAsyncTaskExecutor .

  • The default thread pool drawbacks

    In the thread pool applications, the reference Alibaba java development specifications: the thread pool is not allowed Executors to create, not allowed to use the system default thread pool, ThreadPoolExecutor recommended by the way, this approach allows engineers to develop more explicit thread pool operating rules, to avoid the risk of resource depletion. Executors disadvantages of each method:

  • newFixedThreadPool and newSingleThreadExecutor: The main problem is the accumulation of request processing queue may consume very large memory, even OOM.
  • newCachedThreadPool and newScheduledThreadPool: The problem is the number of threads to the maximum number is Integer.MAX_VALUE, may create a very large number of threads, even OOM.

    @Async default configuration using asynchronous SimpleAsyncTaskExecutor, the default thread pool thread to create a task, if the system ever created thread will eventually lead to system memory for too high, causing an OutOfMemoryError. For thread creates problems, SimpleAsyncTaskExecutor provides current limiting mechanism to control the switch through concurrencyLimit property, open the limiting mechanism when concurrencyLimit> = 0, the current-limiting mechanism that is turned off by default concurrencyLimit = -1, when the case is closed, will continue to create new thread to handle the task. Based on the strict sense of the default configuration, SimpleAsyncTaskExecutor not a thread pool thread up function can not reuse.

4. @Async use a custom thread pool

    Custom thread pool, the system may be of a more fine-grained control thread pool, easy to adjust the size of the thread pool configuration, a thread of execution and exception handling control. When the system is provided instead of the default custom thread pool thread pool, although a plurality of modes may be provided by, but replacing the default thread pool thread pool ultimately produced has a set and only (not provided a plurality of class inheritance AsyncConfigurer). Custom thread pool has the following modes:

  • Re-implement the interface AsyncConfigurer
  • Inheritance AsyncConfigurerSupport
  • Configuring TaskExecutor replaced by a custom built task executor

   Spring calls by viewing the source code default rules on @Async, and will give priority to a query class source code to achieve AsyncConfigurer this interface, to achieve this type of interface is AsyncConfigurerSupport. But the thread pool and asynchronous processing method for the default configuration are empty, so, whether it is inherited or re-implement interfaces are required to specify a thread pool. And re-implement public Executor getAsyncExecutor () method.

(1) implement the interface AsyncConfigurer

@Configuration
public class AsyncConfiguration implements AsyncConfigurer {
    @Bean("kingAsyncExecutor")
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        int corePoolSize = 10;
        executor.setCorePoolSize(corePoolSize);
        int maxPoolSize = 50;
        executor.setMaxPoolSize(maxPoolSize);
        int queueCapacity = 10;
        executor.setQueueCapacity(queueCapacity);
        executor.setRejectedExecutionHandler(new new ThreadPoolExecutor.CallerRunsPolicy ()); 
        String threadNamePrefix = "kingDeeAsyncExecutor-" ; 
        executor.setThreadNamePrefix (threadNamePrefix); 
        executor.setWaitForTasksToCompleteOnShutdown ( to true );
         // use the self-defined cross-thread request thread factory level class 
        RequestContextThreadFactory threadFactory = RequestContextThreadFactory.getDefault (); 
        executor.setThreadFactory (threadFactory); 
        int awaitTerminationSeconds =. 5 ; 
        executor.setAwaitTerminationSeconds (awaitTerminationSeconds); 
        executor.initialize (); 
        return Executor; 
    }

    @Override
    public Executor getAsyncExecutor() {
        return executor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> ErrorLogger.getInstance().log(String.format("执行异步任务'%s'", method), ex);
    }
}

(2) inherited AsyncConfigurerSupport

@Configuration  
@EnableAsync  
class SpringAsyncConfigurer extends AsyncConfigurerSupport {  
  
    @Bean  
    public ThreadPoolTaskExecutor asyncExecutor() {  
        ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();  
        threadPool.setCorePoolSize(3);  
        threadPool.setMaxPoolSize(3);  
        threadPool.setWaitForTasksToCompleteOnShutdown(true);  
        threadPool.setAwaitTerminationSeconds(60 * 15);  
        return threadPool;  
    }  
  
    @Override  
    public Executor getAsyncExecutor() {  
        return asyncExecutor;  
}  

  @Override  
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return (ex, method, params) -> ErrorLogger.getInstance().log(String.format("执行异步任务'%s'", method), ex);
}
}

(3) configure a custom TaskExecutor

Because AsyncConfigurer default thread pool is empty in the source code, Spring by beanFactory.getBean (TaskExecutor.class) to see if there is a thread pool, when not configured by beanFactory.getBean (DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class), and check whether there is a default TaskExecutor name of the thread pool. Therefore, the project can be defined as the name of the bean TaskExecutor generate a default thread pool. May not specify the name of the thread pool, affirming a thread pool itself is based on the underlying TaskExecutor.class can.

such as:

 Executor.class : ThreadPoolExecutorAdapter-> ThreadPoolExecutor-> AbstractExecutorService-> ExecutorService-> the Executor (a mode for the final bottom Executor.class, when replacing the default thread pool, to be set as the default name of TaskExecutor thread pool)
 Executor.class : ThreadPoolTaskExecutor-> SchedulingTaskExecutor-> AsyncTaskExecutor-> of TaskExecutor (a mode for the final bottom TaskExecutor.class, when replacing the default thread pool may designate a thread pool name.)
package intellif.configs;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author liuyu
 * @className TaskConfiguration
 * @date 2019/12/17 11:40
 * @description
 */


@Component
public class TaskConfiguration implements AsyncConfigurer {

    private static Logger logger = LogManager.getLogger(TaskConfiguration.class);

    @Value("${thread.pool.corePoolSize:10}")
    private int corePoolSize;

    @Value("${thread.pool.maxPoolSize:20}")
    private int maxPoolSize;

    @Value("${thread.pool.keepAliveSeconds:4}")
    private int keepAliveSeconds;

    @Value("${thread.pool.queueCapacity:512}")
    private int queueCapacity;

    @Value("${thread.pool.waitForTasksToCompleteOnShutdown:true}")
    private boolean waitForTasksToCompleteOnShutdown;

    @Value("${thread.pool.awaitTerminationSeconds:60}")
    private int awaitTerminationSeconds;


    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数
        executor.setCorePoolSize (corePoolSize);
         // maximum number of threads in the thread pool, only after the buffer queue is full, the application will exceed the number of threads core threads 
        executor.setMaxPoolSize (maxPoolSize);
         // allows threads to idle time, when more than thread outside the core thread, after the idle time of arrival will be destroyed 
        executor.setKeepAliveSeconds (keepAliveSeconds);
         //// used to buffer queue to perform tasks 
        executor.setQueueCapacity (queueCapacity);
         // prefix name of the thread pool, you can positioning the thread pool for processing tasks where 
        executor.setThreadNamePrefix ( "taskExecutor-" );
         // thread pool to reject the task of processing strategies 
        executor.setRejectedExecutionHandler ((Runnable r, ThreadPoolExecutor EXE) -> { 
            logger.warn ( "current task thread pool queue is full. " ); 
        });
        // This method is used to set the thread pool is closed while waiting for after all tasks are complete, then continue to destroy other Bean, so the destruction of these asynchronous tasks will be destroyed before the database connection pool object. 
        executor.setWaitForTasksToCompleteOnShutdown (waitForTasksToCompleteOnShutdown);
         // This method is used to set the thread pool, the task of waiting time, if more than this time not to force the destruction of the destruction, in order to ensure that the application can finally be closed, instead of blocking live. 
        executor.setAwaitTerminationSeconds (awaitTerminationSeconds); 
        executor.initialize (); 
        return Executor; 
    } 

    @Override 
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler () {
         return (EX, Method, the params) -> logger.error ( "thread pool tasks unknown exception occurs." , EX); 
    } 
}
  • Multiple threads pool

   @Async comment, use the system default or user-defined thread pool (instead of the default thread pool). You can set up multiple thread pool in the project, the thread pool name when asynchronous call, indicating the need to call, such as @Async ( "new_task").

5. @Async notes Failure

No past to the proxy class, when this class calls, direct their own internal call, did not take the proxy class

1. No add annotations @EnableAsync notes in class @SpringBootApplication start them.
2. The asynchronous annotation @Async method return value can be void or Future.
3. Spring's proxy did not take the class. Because @Transactional and achieve @Async notes are based on the Spring AOP, to achieve AOP is implemented based on dynamic proxy mode. Notes that the reason of failure is quite clear, it is possible because the calling method is the object itself rather than the proxy object, because there is no Spring container.

Solution:

Here to talk about specific solutions of the third case.
1. Notes must be a public method.
2. The method must be called from another class, which is called from outside the class, the class is called internally is invalid.
3. If you need to call from inside the class, you need to get their proxy class, the following code

@Service
public class AsyncService{
  public void methodA(){
    ...
    AsyncService asyncServiceProxy = SpringUtil.getBean(AsyncService.class);
    asyncServiceProxy .methodB();
    ...
  }
 
  @Async
  public void methodB() {
    ...
  }
}

This class can be defined in this class implemented Multithread asynchronous call this class;

It must be achieved in two ways:

  • public method
  • Manually get spring bean

SpringUtils of tools, manual method to obtain bean:

package intellif.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author liuyu
 * @className SpringUtils
 * @date 2019/12/16 20:55
 * @description
 */

@Component("springContextUtil")
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext = null ; public static the ApplicationContext getApplicationContext () { return applicationContext; } @SuppressWarnings ( "an unchecked" ) public static <T> T the getBean (String the beanId) { return (T) to applicationContext.getBean (the beanId); } public static <T> T the getBean (Class <T> requiredType) { return (T) to applicationContext.getBean (requiredType); } / ** * Spring container after startup, applicationContext will come to an automatic injection, and we applicationContext * assigned to static variables, it is convenient subsequent get container object *@see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtils.applicationContext = applicationContext; } }

 

Reference article:

https://www.cnblogs.com/wlandwl/p/async.html

https://blog.csdn.net/YoungLee16/article/details/88398045

Guess you like

Origin www.cnblogs.com/miracleYu/p/12054431.html