[精品]SpringBoot整合定时任务搭配线程池实例应用

SpringBoot整合定时任务搭配线程池实例应用

*springboot最基本得定时任务Scheduled注解执行任务都是单线程执行,且多个Scheduled注解下的定时任务也都是同一个线程执行的,尤其在多个Scheduled任务的时候,这样是很容易发生线程堵塞的,所以一般我们都会采用自定义线程池ThreadPoolTaskExecutor。

1.首先springboot启动器添加@EnableScheduling开启定时任务

@EnableScheduling

@SpringBootApplication

public class TestScheduledApplication extends SpringBootServletInitializer {

@Override

protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {

return builder.sources(this.getClass());

}

public static void main(String[] args) {

new SpringApplicationBuilder(TestScheduledApplication.class).web(true).run(args);

}

}

2.我们采用@Configuration注解配置线程池类。首先类上加上注解@EnableAsync开启异步支持。

然后定义多个@bean方法配置多个线程池,可以根据任务的业务场景配置不同核心线程,最大线程,任务队列,以及拒绝策略。

@Configuration

@EnableAsync

public class ExecutorConfig1 {

@Bean

public Executor executor1() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setThreadNamePrefix("test-schedule1-");

executor.setMaxPoolSize(20);

executor.setCorePoolSize(15);

executor.setQueueCapacity(0);

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

return executor;

}

@Bean

public Executor executor2() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setThreadNamePrefix("test-schedule2-");

executor.setMaxPoolSize(20);

executor.setCorePoolSize(15);

executor.setQueueCapacity(0);

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

return executor;

}

}

3.@Bean注解生成的对象默认名字和方法名称相同,我们在任务的方法添加Async注解来指定哪个线程池来执行任务。Scheduled注解配置执行时间

// 间隔1秒执行一次

@Async("executor1")

@Scheduled(cron = "0/1 * * * * ?")

public void method1() {

logger.info("——————————method1 start——————————");

logger.info("——————————method1 end——————————");

}

// 间隔1秒执行一次

@Scheduled(cron = "0/1 * * * * ?")

@Async("executor2")

public void method2() {

logger.info("——————————method2 start——————————");

logger.info("——————————method2 end——————————");

}

二、线程池ThreadPoolTaskExecutor 工作原理示意图

三、注意点:

1.当使用Async注解异步执行任务的时候,没有配置自己的线程池,会默认使用SimpleAsyncTaskExecutor。它并不是真正的线程池,不会重用线程,每次执行任务都会开启新的线程

2.Scheduled 和quartz 对比的话,quartz配置比较复杂,需要定义任务类,然后任务类注入到任务工厂容器当中,任务容器传入到执行器的工厂容器,且配置执行时间,最后将触发器注入到任务工厂容器中等待执行。quartz虽然配置复杂,但是支持可视化动态配置定时任务,和在数据库中实现持久化任务

3.关于线程池为什么采用ThreadPoolTaskExecutor线程池。

(1)首先java的线程池顶级接口是Executor。里面只有一个execute(Runnable command)方法;执行一个包含任务的线程类对象,这样目的其实就是将任务模型和执行器分开来解耦合。 而他下面有俩大接口,分别是jdk的JUC包下面的ExecutorService和spring包下面的TaskExecutor接口

(2)jdk自带的线程池在JUC包下面 ThreadPoolExecutor是最底层的线程池,也包含了设置最大线程核心线程,但是一般项目中没有采用spring的情况下才会使用。而spring包下面的ThreadPoolTaskExecutor线程池其实内部也是封装ThreadPoolExecutor和在其方法上进行了一定的增强,增加了任务队列

(3)(包括线程池工具类Executors也是通过静态工厂方法,调用ThreadPoolExecutor的带参数构造方法来创建的常用四大自线程池)

4.JDK默认提供的拒绝策略

(1)有如下几种:

AbortPolicy:直接抛出异常,默认策略

CallerRunsPolicy:用调用者所在的线程来执行任务

DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务

DiscardPolicy:直接丢弃任务

(2).ThreadPoolTaskExecutor extends ExecutorConfigurationSupport,父类中通过三元运算配置拒绝策略,默认AbortPolicy(当队列和最大线程池都满了之后直接抛出异常)

public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { this.rejectedExecutionHandler = (rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy()); }

(3)ThreadPoolExecutor线程池类中定义了四个静态拒绝执行处理器类,而ThreadPoolTaskExecutor 的父类也正是调用了当中的四种策略处理器来完成线程策略

public static class AbortPolicy implements RejectedExecutionHandler {

/**

* Creates an {@code AbortPolicy}.

*/

public AbortPolicy() { }

/**

* Always throws RejectedExecutionException.

*

* @param r the runnable task requested to be executed

* @param e the executor attempting to execute this task

* @throws RejectedExecutionException always

*/

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

throw new RejectedExecutionException("Task " + r.toString() +

" rejected from " +

e.toString());

}

}

四、关于四大线程池底层队列的分析

FixedThreadPool和SingleThreadExecutor:允许请求队列最大长度为Integer.MAX_VALUE,导致OOM;CachedThreadPool和ScheduledThreadPool:允许创建线程的最大数量为Integer.MAX_VALUE,导致OOM。

并发库中有个类是BlockingQueue,就是阻塞队列,这个类提供了两个方法,put()和take(),前者是将对象放到队列里,如果没有空闲节点了,就等待,后者是从头拿对象,如果没有对象了就等到有。FixedThreadPool和SingleThreadExecutor都是采用无界的LinkedBlockingQueue,LinkedBlockingQueue中引入了两把锁,takeLock和putLock,分别用于take和put操作,因为入队和出队用的不同的锁,所以这个队列可以同时入队和出兑。后俩者的队列是SynchronousQueue

线程池对队列的三种状态:无界队列、有界队列、同步移交。

无界队列:请求无限增加,队列也会无限增加,会导致资源耗尽。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值