【Java定时任务】SpringBoot+@Schedule注解

本文详细介绍了如何在SpringBoot应用中使用@Scheduled注解实现定时任务,包括cron表达式的使用、固定间隔和频率定时、以及如何配置线程池以实现并行执行,以避免并发问题。

目录

一、SpringBoot+@Scheduled注解实现定时任务

1-1启用定时任务功能

1-2 添加定时任务

1-2-1 cron表达式

1-2-2 固定间隔定时任务

1-2-3 固定频率定时任务

1-3 并行执行定时任务

1-3-1 修改任务调度器 默认使用的线程池

1-3-2 交给异步线程池处理


一、SpringBoot+@Scheduled注解实现定时任务

当使用SpringBoot框架时,可以使用@Scheduled注解来实现定时任务

1-1启用定时任务功能

在启动类或者配置类上增加@EnableScheduling注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
​
@EnableScheduling
@SpringBootApplication
public class DemoApplication {
​
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

1-2 添加定时任务

@Schedule注解支持cron表达式、固定时间、固定频率三种调度方式

1-2-1 cron表达式

与Linux下定时任务用到的Cron表达式一样

@Scheduled(cron = "0/1 * * * * * *")
public void mytimer(){
    System.out.println("hello world");
}

注意:

【1】cron表达式配置的任务如果执行超时,会从上一个任务结束的时间开始计算间隔

【2】定时器的任务方法不能有返回值

【3】实现类上要有组件的注解@Component,@Service,@Repository

1-2-2 固定间隔定时任务

下一次的任务执行时间是从上一次定时任务结束时间开始计算

@Scheduled(fixedDelay = 2)
public void mytimer(){
    System.out.println("hello world");
}

1-2-3 固定频率定时任务

下一次的任务执行时间是从上一次定时任务开始时间开始计算

@Scheduled(@Scheduled(fixedRate = 2000)
public void mytimer(){
    System.out.println("hello world");
}

1-3 并行执行定时任务

默认状态下,只有一个线程在执行定时任务。

当有多有定时任务时,是串行的,不是并行的!!!

如有两个定时任务,分别设置每两秒执行一次,效果会是这样

Timer1:第0秒执行第一次

Timer2:第2秒执行第一次

Timer1:第4秒执行第二次

Timer2:第6秒执行第二次

.....

这并不是我们想要的效果,所以必须配置定时任务并行执行

1-3-1 修改任务调度器 默认使用的线程池

添加一个configuration,实现SchedulingConfigurer接口就可以了

@Configuration
public class ScheduleConfig implements SchedulingConfigurer{
​
  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    taskRegistrar.setTaskScheduler(getTaskScheduler());
  }
​
  @Bean
  public TaskScheduler getTaskScheduler() {
    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
    taskScheduler.setPoolSize(3);  // 此处为线程池数量
    taskScheduler.setThreadNamePrefix("myworker-");
    taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
    return taskScheduler;
  }
}

1-3-2 交给异步线程池处理

启用@EnableAsync注解,并在每一个定时任务方法上使用@Async注解

@Component
@EnableScheduling
@EnableAsync
public class MyCronTask {
​
  private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class);
​
  @Async
  @Scheduled(fixedDelay = 2000)
  void task1Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task1 execute");
  }

  ​@Async
  @Scheduled(fixedDelay = 2000)
  void task2Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task2 execute");
  }
  
  ​@Async
  @Scheduled(fixedDelay = 2000)
  void task3Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task3 execute");
  }
}

@Async注解在使用时,如果不指定线程池的名称,则使用Spring默认的线程池,Spring默认的线程池为SimpleAsyncTaskExecutor

这个线程池的配置是

    默认核心线程数:8,
    
    最大线程数:Integet.MAX_VALUE,
    队列使用LinkedBlockingQueue,
    容量是:Integet.MAX_VALUE,
    空闲线程保留时间:60s,
    线程池拒绝策略:AbortPolicy。

可以看到,最大线程数使用的是Integer.MAX_VALUE,即对于每一次定时任务的执行都会创建新的线程,并发环境下,会无限创建进程 -> OOM -> 系统崩溃

为了更好控制线程的使用,我们可以自定义线程池。

首先定义一个线程池

@Configuration
public class MyTaskExecutor {
​
  @Bean(name = "myExecutor")
  public TaskExecutor getMyExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(3);
    taskExecutor.setMaxPoolSize(10);
    taskExecutor.setQueueCapacity(20);
    taskExecutor.setThreadNamePrefix("myExecutor-");
    taskExecutor.initialize();
    return taskExecutor;
  }
}

然后在使用@Async注解时指定线程池

@Component
@EnableScheduling
@EnableAsync
public class MyCronTask {
​
  private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class);
​
  @Async("myExecutor")
  @Scheduled(fixedDelay = 2000)
  void task1Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task1 execute");
  }

  @Async("myExecutor")
  @Scheduled(fixedDelay = 2000)
  void task2Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task2 execute");
  }
  
  @Async("myExecutor")
  @Scheduled(fixedDelay = 2000)
  void task3Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task3 execute");
  }
}

用c++/qt写的项目,可供自己学习,项目都经测试过,真实可靠,请放心使用。Qt支持 Windows、Linux/Unix、Mac OS X、Android、BlackBerry、QNX等多种平台,并为这些不同的平台提供了统一的开发环境。面向对象C++是完全面向对象的,这一点和Objective-c等在开发很相似。而Qt又是基于C++一种语言的扩展,大家都知道C++ 有快速、简易、面向对象等很多优点,所以Qt自然也继承者C++这些的优点。Qt良好的封装机制使得Qt的模块化程度非常高,可重用性较好,对用户开发来货是非常方便的。Qt提供一种为signals/slots(信号和槽) 的安全类型来替代callback,使得各个元件之间的协同工作变得十分简单。丰富的APIQt包括多达 250 个以上的 C++ 类,还提供基于模板的 collections, serialization, file, I/Odevice, directory management, date/time 类。甚至还包括正则表达式的处理功能。支持 2D/3D 图形渲染,支持 OpenGL。大量的开发文档。XML支持Webkit 引擎的集成,可以实现本地界面与Web内容的无缝集成,但是真正使得 Qt 在自由软件界的众多 Widgets (如 Lesstif,Gtk,EZWGL,Xforms,fltk 等等)中脱颖而出的还是基于 Qt 的重量级软件 KDE。信号和槽机制Qt提供了信号和槽机制用于完成见面操作的响应,是完成任意两个Qt对象之通信机制。其中,信号会在某个特定情况或动作下被触动,槽是等同于接受并处理信号的函数。为什么方法不是直接调用的。中间用到 Signal 和槽机制不是多此一举?其实在我们生活也是一样,老板级别的好说话,老板给助理分派任务也好说话,但是助理给老板分任务,可想而知会有什么后果,在以前的统治阶层肯定不允许这样的事发生。所以在分层思想中,我们所调用的函数也是这样的,上层可以调用下层和同一层的函数,下层函数不可以调用上层函数,否则程序的层次性会被打破,导致结构错综复杂,难以维护和管理。那么怎样才能做到向上管理呢,有任务分配给老板怎么办?老板会设立一个机构,也就是一个函数,用无限循环来查询助理的状态,如果助理真的有事情,这个机构就把这消息拿到老板来处理。但是这种处理方式显得有些复杂,我们想要的简单明了的方式是,如果助理有事件发生,可以直接调用老板函数处理。说了这么多其实就是想说,信号和槽的最大优势在于,它完善了程序分层的思想,可以在不改变程序的层次性的情况下,完成由下层到上层的调用。在下层发出一个 Signal,这时上层与其想关联的 Slot 函数就会响应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值