记录Spring boot首次使用定时任务和多线程
在工作过程中,遇到需要通过定时器进行数据更新,以及通过多线程加快远程服务器配置文件的更新速度的需求,对于一个刚工作的新人也是一个不小的挑战,由于之前对于这些知识只是简单的使用,同时也没有通过spring boot进行实现,特此记录便于自己查阅。
关于定时器的实现,在spring boot中有专门的实现,通过注解开发,大大减轻了工作量。spring boot中提供了两种调度方式。第一种是串行的方式,使用也较简单。首先在spring boot的程序入口通过@EnableScheduling开启总开关。
@SpringBootApplication
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
之后在需要定时执行的方法上添加@Scheduled注解即可,注意方法所在的类要被spring boot可以扫描到,也就是加上类似@Compont、@Service等注解如下图:
@Scheduled(cron = "0/2 * * * * *")
public void timer(){
........
}
其中,cron中设置的字符串是cron表达式,用来设置改方法执行的时间间隔,程序启动后便会定时执行。读者需要的话可以自学一下cron表达式,网上也有很多在线生成cron表达式的工具、网页等。cron表达式,有专门的语法,而且感觉有点绕人,不过简单来说,大家记住一些常用的用法即可,特殊的语法可以单独去查。cron一共有7位,但是最后一位是年,可以留空,所以我们可以写6位:
- 第一位,表示秒,取值0-59
- 第二位,表示分,取值0-59
- 第三位,表示小时,取值0-23
- 第四位,日期天/日,取值1-31
- 第五位,日期月份,取值1-12
- 第六位,星期,取值1-7,星期一,星期二...,不是第1周,第二周的意思 ,注意:1表示星期天,2表示星期一。
- 第7为,年份,可以留空,取值1970-2099
定时器的第二种实现方式为并行的方式。当定时任务很多的时候,为了提高任务执行效率,可以采用并行方式执行定时任务,任务之间互不影响, 通过自定义类实现SchedulingConfigurer接口就可以,重写configureTasks方法,为了让spring boot可扫描到该类,同样需要在类上定义注解,具体执行周期同样通过cron表达式来定义,具体如下:
@Component
public class ScheduleTask implements SchedulingConfigurer{
@Autowired
private Service serviceImpl;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// TODO Auto-generated method stub
taskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
serviceImpl.save();
}
}, new Trigger() {
@SuppressWarnings("boxing")
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cron="0/3 * * * * *";
CronTrigger trigger = new CronTrigger(cron); // 定时任务触发,可修改定时任务的执行周期
Date nextExecDate = trigger.nextExecutionTime(triggerContext);
return nextExecDate;
}
}
});
}
}
以上为spring boot定时器的实现方法,我采用的第二种方法。
在spring boot中实现多线程的使用,我采用的是下述方法,对于多线程的使用,后面我还需要多多学习。
首先编写配置类AsyncConfig,
@SpringBootConfiguration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer{
@Override
public Executor getAsyncExecutor() {
// ThredPoolTaskExcutor的处理流程
// 当池子大小小于corePoolSize,就新建线程,并处理请求
// 当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去workQueue中取任务并处理
// 当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
// 当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁
ThreadPoolTaskExecutor threadPoolTaskExecutor=new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(5);//核心池大小
threadPoolTaskExecutor.setMaxPoolSize(10);//最大池大小
threadPoolTaskExecutor.setQueueCapacity(25);
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// TODO Auto-generated method stub
return null;
}
}
在配置类上通过@SpringBootConfiguration表明是一个配置类,通过@EnableAsync开启多线程的开关。然后编写自己的线程类执行具体线程的代码。在类上通过@service注解使得spring boot可以扫描到该类,进而进行管理,在方法上通过@Async注解定义一个线程,只要通过自动注入该类的实例,调用这个方法就会开启一个线程去执行具体定义的任务。
@Service
public class PersonalThread {
@SuppressWarnings("boxing")
@Async
public void personalRun() {
//线程执行的代码
}
}
通过PersonalThread类的对象调用响应的方法来开启线程,每次调用开启一个线程。
personalThread.personalRun();
在具体操作的过程中,还遇到了一个问题,由于开启定时器与多线程进行的操作都涉及到对数据库的更新操作,这样自然而然就会出现多线程中经常遇到的数据更新不一致问题,更新结果经常出问题,于是刚开始我采取在进行保存操作时将定时器暂停,保存之后在开启定时器的方法,但是这种方法貌似定时器在执行完当前周期才会停,没有立即停掉,这样会增加保存操作的延时,而且貌似数据更新还是存在问题......,于是目前我采用单例定义了一个对象,通过加锁的方式保证数据更新的正确性,但是还是存在问题,这样处理的结果导致更新操作由于需要获得锁而有不确定的延时,但是好歹数据保存结果执行正确,读者有什么好方法可以告诉我哈,本人菜鸟一个,求指教~
程序之路漫漫,吾将上下而求索
上一篇: MSSQL 检查所使用的语句是否符合标准
下一篇: 异步获取评论者网站截图