欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

记录Spring boot首次使用定时任务和多线程

程序员文章站 2022-05-01 16:29:43
...

       在工作过程中,遇到需要通过定时器进行数据更新,以及通过多线程加快远程服务器配置文件的更新速度的需求,对于一个刚工作的新人也是一个不小的挑战,由于之前对于这些知识只是简单的使用,同时也没有通过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位:

  1. 第一位,表示秒,取值0-59
  2. 第二位,表示分,取值0-59
  3. 第三位,表示小时,取值0-23
  4. 第四位,日期天/日,取值1-31
  5. 第五位,日期月份,取值1-12
  6. 第六位,星期,取值1-7,星期一,星期二...,不是第1周,第二周的意思 ,注意:1表示星期天,2表示星期一
  7. 第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();

 

        在具体操作的过程中,还遇到了一个问题,由于开启定时器与多线程进行的操作都涉及到对数据库的更新操作,这样自然而然就会出现多线程中经常遇到的数据更新不一致问题,更新结果经常出问题,于是刚开始我采取在进行保存操作时将定时器暂停,保存之后在开启定时器的方法,但是这种方法貌似定时器在执行完当前周期才会停,没有立即停掉,这样会增加保存操作的延时,而且貌似数据更新还是存在问题......,于是目前我采用单例定义了一个对象,通过加锁的方式保证数据更新的正确性,但是还是存在问题,这样处理的结果导致更新操作由于需要获得锁而有不确定的延时,但是好歹数据保存结果执行正确,读者有什么好方法可以告诉我哈,本人菜鸟一个,求指教~

        程序之路漫漫,吾将上下而求索