Spring asynchronous annotation @Async and thread pool configuration (source code analysis)

Spring asynchronous annotation @Async and thread pool configuration

Using the @Async annotation, the SimpleAsyncTaskExecutor thread pool is used by default. This thread pool is not a real thread pool. Using this thread pool cannot achieve thread reuse, and a new thread will be created every time it is called. If the system continues to create threads, it will eventually cause the system to occupy too much memory and cause an OutOfMemoryError error

SimpleAsyncTaskExecutor source code analysis (some code omitted)

public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
		implements AsyncListenableTaskExecutor, Serializable {
    
    

	//ps: 这个参数值在 AsyncListenableTaskExecutor 的父类 AsyncTaskExecutor 里面
	/** Constant that indicates no time limit. */
	long TIMEOUT_INDEFINITE = Long.MAX_VALUE;

	//ps: execute 方法只有两个,不带时间的方法的调用带时间的,并且这个时间是Long类型的最大值
	/**
	 * Executes the given task, within a concurrency throttle
	 * if configured (through the superclass's settings).
	 * @see #doExecute(Runnable)
	 */
	@Override
	public void execute(Runnable task) {
    
    
		execute(task, TIMEOUT_INDEFINITE);
	}

	//ps: 调用这个方法,不管判断逻辑怎样,最终都会调用 doExecute 方法
	/**
	 * Executes the given task, within a concurrency throttle
	 * if configured (through the superclass's settings).
	 * <p>Executes urgent tasks (with 'immediate' timeout) directly,
	 * bypassing the concurrency throttle (if active). All other
	 * tasks are subject to throttling.
	 * @see #TIMEOUT_IMMEDIATE
	 * @see #doExecute(Runnable)
	 */
	@Override
	public void execute(Runnable task, long startTimeout) {
    
    
		Assert.notNull(task, "Runnable must not be null");
		Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);
		if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
    
    
			this.concurrencyThrottle.beforeAccess();
			doExecute(new ConcurrencyThrottlingRunnable(taskToUse));
		}
		else {
    
    
			doExecute(taskToUse);
		}
	}

	//ps: doExecute 方法只有一个,所有的最终都是回到这个方法, 最终调用 thread.start() 来启动这个线程,最后被分配时间片去执行run方法
	//可以看到,不管怎样都是新建一个线程,只不过是新建线程的方式不一样, 如果线程工厂类不为空就使用工厂创建线程,否则使用父类的创建线程的方法来创建线程
	/**
	 * Template method for the actual execution of a task.
	 * <p>The default implementation creates a new Thread and starts it.
	 * @param task the Runnable to execute
	 * @see #setThreadFactory
	 * @see #createThread
	 * @see java.lang.Thread#start()
	 */
	protected void doExecute(Runnable task) {
    
    
		Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
		thread.start();
	}


	//ps: 这个方法在当前类的父类 CustomizableThreadCreator 里面
	//这个方法是新建一个线程
	/**
	 * Template method for the creation of a new {@link Thread}.
	 * <p>The default implementation creates a new Thread for the given
	 * {@link Runnable}, applying an appropriate thread name.
	 * @param runnable the Runnable to execute
	 * @see #nextThreadName()
	 */
	public Thread createThread(Runnable runnable) {
    
    
		Thread thread = new Thread(getThreadGroup(), runnable, nextThreadName());
		thread.setPriority(getThreadPriority());
		thread.setDaemon(isDaemon());
		return thread;
	}

}

Let's take a look at the partial implementation of @Async asynchronous annotations.
By finding @EnableAsync, we can see that the document that enables asynchronous annotations clearly states that the thread pool used by asynchronous annotations is org.springframework.core.task.SimpleAsyncTaskExecutor,

There is also a sentence will be used to process async method invocations, saying that this thread pool is used to process asynchronous methods
insert image description here

The configuration interface class AsyncConfigurer for asynchronous processing
insert image description here

AsyncConfigurer implementation class AsyncConfigurerSupport
insert image description here

Next, customize the asynchronous thread pool

Implement the asynchronous thread pool configuration interface class, change the default thread pool configuration to a custom ThreadPoolTaskExecutor

package com.biostime.material.purchase.config.log;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

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

/**
 * @Description Async 异步注解线程配置, AsyncConfigurer 默认配置实现,默认的线程池 SimpleAsyncTaskExecutor
 * @author 21506 HuangXiang
 * @since 2021/12/1
 */
@Configuration
public class AsyncConfiguration implements AsyncConfigurer {
    
    

    private static final Logger LOGGER = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

    //线程池配置
    @Bean(name = "asyncPoolTaskExecutor")
    public ThreadPoolTaskExecutor executor(){
    
    
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();

        //核销线程数
        taskExecutor.setCorePoolSize(2);
        //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        taskExecutor.setMaxPoolSize(10);
        //缓存队列
        taskExecutor.setQueueCapacity(50);
        //允许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        taskExecutor.setKeepAliveSeconds(200);
        //异步方法内部线程名称
        taskExecutor.setThreadNamePrefix("async-");
        /**
         * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
         * 通常有以下四种策略:
         * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
         * ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
         * ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
         * ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
         */
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();

        return taskExecutor;

    }


    //指定默认线程池
    @Override
    public Executor getAsyncExecutor() {
    
    
        return executor();
    }

    //线程异常处理
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    
    

        return (ex , method , params) ->
                LOGGER.error("线程池执行任务发送未知错误,执行方法:{}",method.getName(),ex);
    }
}

Next, the asynchronous thread call will use the custom thread pool!

Guess you like

Origin blog.csdn.net/HX0326CSDN/article/details/121655391