Spring 异步注解 @Async 与线程池配置(源码解析)

Spring 异步注解 @Async 与线程池配置

使用@Async注解,在默认情况下用的是SimpleAsyncTaskExecutor线程池,该线程池不是真正意义上的线程池,使用此线程池无法实现线程重用,每次调用都会新建一条线程。若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误

SimpleAsyncTaskExecutor 源码解析(省略一部分代码)

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

}

再来看看 @Async 异步注解的部分实现
通过找到 @EnableAsync 看到,这个启用异步注解的文档里面明确写到, 异步注解使用的线程池是 org.springframework.core.task.SimpleAsyncTaskExecutor ,

还有一句 will be used to process async method invocations , 说的是这个线程池用于处理异步方法
在这里插入图片描述

异步处理的配置接口类 AsyncConfigurer
在这里插入图片描述

AsyncConfigurer 的实现类 AsyncConfigurerSupport
在这里插入图片描述

接下来自定义异步线程池

实现异步线程池配置接口类,把默认线程池配置改变为自定义的 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);
    }
}

接下来异步线程调用就会使用自定义的线程池!

猜你喜欢

转载自blog.csdn.net/HX0326CSDN/article/details/121655391