Anotación asincrónica de Spring @Async y configuración del grupo de subprocesos (análisis de código fuente)

Anotación asincrónica de Spring @Async y configuración del grupo de subprocesos

Usando la anotación @Async, el grupo de subprocesos SimpleAsyncTaskExecutor se usa de forma predeterminada. Este grupo de subprocesos no es un grupo de subprocesos en el verdadero sentido. El uso de este grupo de subprocesos no puede lograr la reutilización de subprocesos y cada llamada creará un nuevo subproceso. Si el sistema continúa creando subprocesos, eventualmente hará que el sistema ocupe demasiada memoria y provocará un error OutOfMemoryError.

Análisis del código fuente de SimpleAsyncTaskExecutor (se omite parte del código)

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;
	}

}

Echemos un vistazo a la implementación parcial de las anotaciones asincrónicas @Async.
Al encontrar @EnableAsync, podemos ver que el documento que permite las anotaciones asincrónicas establece claramente que el grupo de subprocesos utilizado por las anotaciones asincrónicas es org.springframework.core.task.SimpleAsyncTaskExecutor.

También hay una oración que se usará para procesar invocaciones de métodos asincrónicos, que dice que este grupo de subprocesos se usa para procesar métodos asincrónicos.
inserte la descripción de la imagen aquí

La clase de interfaz de configuración AsyncConfigurer para procesamiento asincrónico
inserte la descripción de la imagen aquí

Clase de implementación AsyncConfigurer AsyncConfigurerSupport
inserte la descripción de la imagen aquí

A continuación, personalice el grupo de subprocesos asincrónicos.

Implemente la clase de interfaz de configuración del grupo de subprocesos asíncronos, cambie la configuración predeterminada del grupo de subprocesos a un ThreadPoolTaskExecutor personalizado

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);
    }
}

A continuación, la llamada de subprocesos asincrónicos utilizará el grupo de subprocesos personalizado.

Supongo que te gusta

Origin blog.csdn.net/HX0326CSDN/article/details/121655391
Recomendado
Clasificación