SpringBoot使用@Async异步处理任务

spring中有个十分优秀的支持,就是注解@EnableAsync就可以使用多线程,@Async加在线程任务的方法上(需要异步执行的任务),定义一个线程任务,通过spring提供的ThreadPoolTaskExecutor就可以使用线程池。

一、首先定义配置类

这个配置类需要实现AsyncConfiguer接口,并实现它的方法,2个方法所对应的作用是

1、异步线程的执行者,在里面配置自动执行的东西,比如线程池参数
2、AsyncUncaughtExceptionHandler异常处理

@Configuration
public class TreadPoolConfigTest implements AsyncConfigurer {

    private static final Logger logger = LoggerFactory.getLogger(TreadPoolConfigTest.class);

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程池数量,方法: 返回可用处理器的Java虚拟机的数量。
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        executor.setCorePoolSize(availableProcessors);
        //最大线程数量
        int maxavailableProcessors = Runtime.getRuntime().availableProcessors() * 5;
        executor.setMaxPoolSize(maxavailableProcessors);

        //线程池的队列容量
        int queueNumber = Runtime.getRuntime().availableProcessors() * 2;
        executor.setQueueCapacity(queueNumber);
        //线程名称的前缀
        executor.setThreadNamePrefix("treadpoolconfigtest--");

        // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
        //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable ex, Method method, Object... params) {
                logger.error("Exception occurs in async method", ex.getMessage());
            }
        };
    }
}

注意:

提交一个新的任务的执行流程
1.1 首先线程池判断核心线程池(corePoolSize)是否已满?没满,则使用核心线程来执行任务。满了,则进入1.2;
1.2 判断工作队列(workQueue)是否已满?没满,则将新提交的任务存储在工作队列里,等核心线程空出再从队列中取。满了,则进入1.3;
1.3 判断整个线程池(maximumPoolSize)是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务;

2、在拒绝策略中Reject策略预定义有四种:
(1)ThreadPoolExecutor.AbortPolicy策略,是默认的策略,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
(2)ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃.
(3)ThreadPoolExecutor.DiscardPolicy策略,不能执行的任务将被丢弃.
(4)ThreadPoolExecutor.DiscardOldestPolicy策略,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

3、getAsyncUncaughtExceptionHandler方法只会处理IllegalArgumentException类型的异常。

二、线程任务类

@Component
public class TreadTasks {
    @Async
    public void startMyTreadTask() {
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
        System.out.println("this is my async task");
    }
}

三、调用异步线程任务

@RestController
@RequestMapping("/asyn")
public class AsyncTaskUse {

    @Autowired
    private TreadTasks treadTasks;

    @GetMapping("/startMysync")
    public String useMySyncTask(int number) {
        for(int i=0;i

然后在springboot启动类上加上@EnableAsync

@SpringBootApplication
@EnableRetry
@EnableAsync
public class SpringbootMysqlMybatisDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootMysqlMybatisDemoApplication.class, args);
    }
}

说明下:我本地运行时核心线程数,队列容量,最大线程数分别是

  核心线程数 队列容量 最大线程数
数量 8 16 40

通过请求参数number来控制异步线程数量。

number数量 核心线程数 队列线程数 非核心线程数 同时任务执行数
1 1 0 0 1
8 8 0 0 8
9 8 1 0 8
24 8 16 0 8
25 8 16 1 9
26 8 16 2 10
56 8 16 32 40

57

8 16 32 40但是会跑出eror

当number=56时,此时在执行的任务有40个,还是16个队列中,只有当线程空出会,才会从队列中去取,number=57时,此时有一个线程数多了,就超出了最大线程数,就会抛出error,在spring的源码中,当超出线程数时,会抛出RejectedExecutionException。

@Override
	public  Future submit(Callable task) {
		ExecutorService executor = getThreadPoolExecutor();
		try {
			return executor.submit(task);
		}
		catch (RejectedExecutionException ex) {
			throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
		}
	}

注意这个异常我们自定的TreadPoolConfigTest#getAsyncUncaughtExceptionHandler方法是捕获不了的,TreadPoolConfigTest#getAsyncUncaughtExceptionHandler只能捕获IllegalArgumentException异常。

如果不想抛出异常,且又想让多出的任务等待执行,可以配置拒绝策略,把getAsyncExecutor方法中的这行注释打开

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

作者:Mango_yes
链接:https://www.jianshu.com/p/a4b4f3df4992
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(其他)