Spring默认线程池 simpleAsyncTaskExecutor
Spring异步线程池的接口类是TaskExecutor ,本质还是 java.util.concurrent.Executor,没有配置的情况下,默认使用的是 simpleAsyncTaskExecutor
@Component
@EnableAsync
public class ScheduleTask {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Async@Scheduled(fixedRate = 2000)public void testScheduleTask() {try {Thread.sleep(6000);System.out.println("Spring1自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}}@Async@Scheduled(cron = "*/2 * * * * ?")public void testAsyn() {try {Thread.sleep(1000);System.out.println("Spring2自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (Exception ex) {ex.printStackTrace();}}
}
输出结果:从运行结果可以看出Spring默认的@Async用线程池名字为SimpleAsyncTaskExecutor,而且每次都会重新创建一个新的线程,所以可以看到TaskExecutor-后面带的数字会一直变大。
SimpleAsyncTaskExecutor特点是每一次执行任务时,他会重新启动一个新的线程,并允许开发者控制并发线程的最大数量,从而起到一定的资源节流作用,默认是concurrencyLimit取值为 -1,即不启用资源节流。
ThreadPoolTaskExecutor配置(Java.util.concurrent.ThreadPoolExecutor)
ThreadPoolTaskExecutor配置,要么用配置文件要么用config
# 核心线程池数
spring.task.execution.pool.core-size=5
# 最大线程池数
spring.task.execution.pool.max-size=10
# 任务队列的容量
spring.task.execution.pool.queue-capacity=5
# 非核心线程的存活时间
spring.task.execution.pool.keep-alive=60
# 线程池的前缀名称
spring.task.execution.thread-name-prefix=god-jiang-task-
@Configuration
public class AsyncScheduledTaskConfig {@Value("${spring.task.execution.pool.core-size}")private int corePoolSize;@Value("${spring.task.execution.pool.max-size}")private int maxPoolSize;@Value("${spring.task.execution.pool.queue-capacity}")private int queueCapacity;@Value("${spring.task.execution.thread-name-prefix}")private String namePrefix;@Value("${spring.task.execution.pool.keep-alive}")private int keepAliveSeconds;@Beanpublic Executor myAsync() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//最大线程数executor.setMaxPoolSize(maxPoolSize);//核心线程数executor.setCorePoolSize(corePoolSize);//任务队列的大小executor.setQueueCapacity(queueCapacity);//线程前缀名executor.setThreadNamePrefix(namePrefix);//线程存活时间executor.setKeepAliveSeconds(keepAliveSeconds);/*** 拒绝处理策略* CallerRunsPolicy():交由调用方线程运行,比如 main 线程。* AbortPolicy():直接抛出异常。* DiscardPolicy():直接丢弃。* DiscardOldestPolicy():丢弃队列中最老的任务。*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//线程初始化executor.initialize();return executor;}
}
在方法上添加@Async注解,然后还需要在@SpringBootApplication启动类或者@Configuration注解类上添加注解@EnableAsync启动多线程注解,@Async就会对标注的方法开启异步多线程调用,注意,这个方法的类一定要交给Spring容器来管理。
@Component
@EnableAsync
public class ScheduleTask {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Async("myAsync")@Scheduled(fixedRate = 2000)public void testScheduleTask() {try {Thread.sleep(6000);System.out.println("Spring1自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}}@Async("myAsync")@Scheduled(cron = "*/2 * * * * ?")public void testAsyn() {try {Thread.sleep(1000);System.out.println("Spring2自带的线程池" + Thread.currentThread().getName() + "-" + sdf.format(new Date()));} catch (Exception ex) {ex.printStackTrace();}}
}
注解的方法必须是public方法
方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的,因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。
异步方法使用注解@Async的返回值只能为void或者Future
Spring线程池的拒绝策略和处理流程
rejectedExectutionHandler参数字段用于配置绝策略,常用拒绝策略如下:AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException
CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。
DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。
DiscardPolicy:用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。
查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第二步。
查看任务队列是否已满,不满就将任务存储在任务队列中,否则执行第三步。
查看线程池是否已满,即就是是否达到最大线程池数,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。