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