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<number;++i){
            treadTasks.startMyTreadTask();
        }
        return "ok";
    }
}

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

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

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

 核心线程数队列容量最大线程数
数量81640

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

number数量核心线程数队列线程数非核心线程数同时任务执行数
11001
88008
98108
2481608
2581619
26816210
568163240

57

8163240但是会跑出eror

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

@Override
	public <T> Future<T> submit(Callable<T> 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
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值