Java学习教程之定时任务全家桶

这篇文章主要给大家介绍了关于Java学习教程之定时任务全家桶的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧!

定时任务应用非常广泛,Java提供的现有解决方案有很多。

本次主要讲schedule、quartz、xxl-job、shedlock等相关的代码实践。

一、SpringBoot使用Schedule

核心代码:

 1 @Component
 2 public class ScheduleTask {
 3 
 4  private Logger logger = LoggerFactory.getLogger(ScheduleTask.class);
 5 
 6  @Scheduled(cron = "0/1 * * * * ? ")
 7  public void one() {
 8 
 9  logger.info("one:" + new Date());
10  }
11 
12  @Scheduled(cron = "0/1 * * * * ? ")
13  public void two() {
14 
15  logger.info("two:" + new Date());
16  }
17 
18 
19  @Scheduled(cron = "0/1 * * * * ? ")
20  public void three() {
21 
22  logger.info("three:" + new Date());
23  }
24 }

运行效果如下:

 

 除此之外还可以这样实现,核心代码:

 1 @PropertySource(value = {
 2  "classpath:task.properties",
 3 }, encoding = "utf-8")
 4 @Component("scheduleTask")
 5 public class ScheduleTask implements SchedulingConfigurer {
 6 
 7 
 8  @Value("${TEST_JOB_TASK_CRON}")
 9  private String cron;
10 
11  @Override
12  public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
13 
14  scheduledTaskRegistrar.addTriggerTask(new Runnable() {
15 
16   @Override
17   public void run() {
18   System.out.println("执行任务:" + DateUtil.date());
19 
20 
21   }
22 
23  }, new Trigger() {
24   @Override
25   public Date nextExecutionTime(TriggerContext triggerContext) {
26   return new CronTrigger(cron).nextExecutionTime(triggerContext);
27   }
28  });
29  }
30 
31  public void setCron(String cron) {
32  this.cron = cron;
33  }
34 }

有朋友或许很疑惑,为什么要写这么一大堆,这个与前面的代码又有何区别呢?

区别是多线程并行。其实多线程并行也可以不用这么写,只需写一段核心配置类代码即可。

定时任务多线程配置类:

1 @Configuration
2 public class ScheduleConfig implements SchedulingConfigurer {
3 
4  public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
5  scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
6  }
7 }

再次启动,查看效果,如下:

 

 

由此看出走不同的线程执行,不同的线程执行的好处是,如果某一个线程挂掉后,并不会阻塞导致其它定时任务无法执行。

另外如果要想并发执行,前面的配置可以不要,直接用SpringBoot提供的现成注解即可,核心代码如下:

 1 @Component
 2 @EnableAsync
 3 public class ScheduleAsyncTask {
 4 
 5  private Logger logger = LoggerFactory.getLogger(ScheduleAsyncTask.class);
 6 
 7  @Scheduled(cron = "0/1 * * * * ? ")
 8  @Async
 9  public void one() {
10 
11  logger.info("one Async:" + new Date());
12  }
13 
14  @Scheduled(cron = "0/1 * * * * ? ")
15  @Async
16  public void two() {
17 
18  logger.info("two Async:" + new Date());
19  }
20 
21 
22  @Scheduled(cron = "0/1 * * * * ? ")
23  @Async
24  public void three() {
25 
26  logger.info("three Async:" + new Date());
27  }
28 }

除此外,还有基于schedule动态定时任务(所谓动态只不过是指cron表达式放在对应的数据表里),简单示例代码:

 1 @Configuration
 2 public class DynamicScheduleTask implements SchedulingConfigurer {
 3 
 4  @Autowired
 5  @SuppressWarnings("all")
 6  CronMapper cronMapper;
 7 
 8  @Mapper
 9  public interface CronMapper {
10  @Select("select cron from cron limit 1")
11  public String getCron();
12  }
13 
14  /**
15  * 执行定时任务.
16  */
17  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
18 
19  taskRegistrar.addTriggerTask(
20   //1.添加任务内容(Runnable)
21   () -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),
22   //2.设置执行周期(Trigger)
23   triggerContext -> {
24    //2.1 从数据库获取执行周期
25    String cron = cronMapper.getCron();
26    //2.2 合法性校验.
27    if (StringUtils.isEmpty(cron)) {
28    // Omitted Code ..
29    }
30    //2.3 返回执行周期(Date)
31    return new CronTrigger(cron).nextExecutionTime(triggerContext);
32   }
33  );
34  }
35 
36 
37 
38 }

核心配置文件(application.yml):

1 spring:
2  datasource:
3  url: jdbc:mysql://127.0.0.1:3306/test
4  username: root
5  password: 1234

SQL脚本:

1 DROP DATABASE IF EXISTS `test`;
2 CREATE DATABASE `test`;
3 USE `test`;
4 DROP TABLE IF EXISTS `cron`;
5 CREATE TABLE `cron` (
6  `cron_id` varchar(30) NOT NULL PRIMARY KEY,
7  `cron` varchar(30) NOT NULL 
8 );
9 INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');

运行效果如下:

 

 

二、SpringBoot使用Quartz

1.Maven依赖

1
2
3
4
5
<!--引入quartz定时框架-->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2.配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
spring:
 quartz:
 #相关属性配置
 properties:
  org:
  quartz:
   scheduler:
   instanceName: clusteredScheduler
   instanceId: AUTO
   jobStore:
   class: org.quartz.impl.jdbcjobstore.JobStoreTX
   driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
   tablePrefix: QRTZ_
   isClustered: true
   clusterCheckinInterval: 10000
   useProperties: false
   threadPool:
   class: org.quartz.simpl.SimpleThreadPool
   threadCount: 10
   threadPriority: 5
   threadsInheritContextClassLoaderOfInitializingThread: true
 #数据库方式
 job-store-type: jdbc
 #初始化表结构
 jdbc:
  initialize-schema: always
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/test
 username: root
 password: 1234

3.启动类

1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
@EnableScheduling
public class BlogQuartzApplication {
 
 public static void main(String[] args) {
  SpringApplication.run(BlogQuartzApplication.class, args);
 }
 
 
}

4.配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class QuartzConfiguration {
 // 使用jobDetail包装job
 @Bean
 public JobDetail myCronJobDetail() {
  return JobBuilder.newJob(CouponTimeOutJob.class).withIdentity("couponTimeOutJob").storeDurably().build();
 }
 
 // 把jobDetail注册到Cron表达式的trigger上去
 @Bean
 public Trigger CronJobTrigger() {
  CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
 
  return TriggerBuilder.newTrigger()
    .forJob(myCronJobDetail())
    .withIdentity("CouponTimeOutJobTrigger")
    .withSchedule(cronScheduleBuilder)
    .build();
 }
}

5.定时任务类

1
2
3
4
5
6
public class CouponTimeOutJob extends QuartzJobBean {
 @Override
 protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
  System.out.println("定时任务执行");
 }
}

6.启动成功不报错

(1)对应的数据库会生成定时任务相关的数据表

 

 

(2)控制台不断输出定时任务执行日志

 

 

 

 

三、SpringBoot使用xxl-job

之前写过一样的例子,如今简化了下。

关于xxl-job使用详情,可以参考我的这篇文章:

SpringBoot整合Xxl-Job

1.Maven依赖

1
2
3
4
5
<dependency>
 <groupId>com.xuxueli</groupId>
 <artifactId>xxl-job-core</artifactId>
 <version>2.2.0</version>
</dependency>

2.配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Configuration
public class XxlJobConfig {
 private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
 
 @Value("${xxl.job.admin.addresses}")
 private String adminAddresses;
 
 @Value("${xxl.job.executor.appname}")
 private String appName;
 
 @Value("${xxl.job.executor.ip}")
 private String ip;
 
 @Value("${xxl.job.executor.port}")
 private int port;
 
 @Value("${xxl.job.accessToken}")
 private String accessToken;
 
 @Value("${xxl.job.executor.logpath}")
 private String logPath;
 
 @Value("${xxl.job.executor.logretentiondays}")
 private int logRetentionDays;
 
 
 @Bean(initMethod = "start", destroyMethod = "destroy")
 public XxlJobSpringExecutor xxlJobExecutor() {
  logger.info(">>>>>>>>>>> xxl-job config init.");
  XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
  xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
  xxlJobSpringExecutor.setAppname(appName);
  xxlJobSpringExecutor.setIp(ip);
  xxlJobSpringExecutor.setPort(port);
  xxlJobSpringExecutor.setAccessToken(accessToken);
  xxlJobSpringExecutor.setLogPath(logPath);
  xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
 
  return xxlJobSpringExecutor;
 }
 
}

3.配置文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# web port
server.port=8081
# no web
#spring.main.web-environment=false
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### xxl-job, access token
xxl.job.accessToken=
### xxl-job executor appname
xxl.job.executor.appname=blog-job-xxl-job
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
xxl.job.executor.address=
### xxl-job executor server-info
xxl.job.executor.ip=
xxl.job.executor.port=8888
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays=30

4.定时任务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class XxlJobTaskExample {
 
 
 @XxlJob("blogJobHandler")
 public ReturnT<String> blogJobHandler(String param) throws Exception {
  System.out.println("执行");
  XxlJobLogger.log("XXL-JOB, Hello World.");
 
  for (int i = 0; i < 5; i++) {
   XxlJobLogger.log("beat at:" + i);
   TimeUnit.SECONDS.sleep(2);
  }
  return ReturnT.SUCCESS;
 }
 
}

5.执行效果

分别如下所示:

 

 

 

 

 

四、SpringBoot使用ShedLock

1.导入Maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 分布式定时任务锁 -->
 <!-- https://mvnrepository.com/artifact/net.javacrumbs.shedlock/shedlock-spring -->
 <dependency>
  <groupId>net.javacrumbs.shedlock</groupId>
  <artifactId>shedlock-spring</artifactId>
  <version>4.0.4</version>
 </dependency>
 <!-- 使用redis做分布式任务 -->
 <dependency>
  <groupId>net.javacrumbs.shedlock</groupId>
  <artifactId>shedlock-provider-redis-spring</artifactId>
  <version>2.5.0</version>
 </dependency>
 <!-- redis组件 -->
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

2.编写配置类

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "PT30M")
public class ShedLockConfig {
 
 @Bean
 public LockProvider lockProvider(RedisTemplate redisTemplate) {
  return new RedisLockProvider(redisTemplate.getConnectionFactory());
 
 }
 
}

3.编写具体的定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class TaskSchedule {
 
 /**
  * 每分钟执行一次
  * [秒] [分] [小时] [日] [月] [周] [年]
  */
 @Scheduled(cron = "1 * * * * ?")
 @SchedulerLock(name = "synchronousSchedule")
 public void SynchronousSchedule() {
   
  System.out.println("Start run schedule to synchronous data:" + new Date());
 
 }
}

4.编写启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableScheduling
public class ShedLockRedisApplication {
 public static void main(String[] args) {
  SpringApplication.run(ShedLockRedisApplication.class);
 }
}

5.配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server:
 tomcat:
 uri-encoding: UTF-8
 max-threads: 1000
 min-spare-threads: 30
 port: 8083
 
spring:
 redis:
 database: 0
 host: localhost
 port: 6379
 password:  # 密码(默认为空)
 timeout: 6000ms # 连接超时时长(毫秒)
 jedis:
  pool:
  max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
  max-wait: -1ms  # 连接池最大阻塞等待时间(使用负值表示没有限制)
  max-idle: 10  # 连接池中的最大空闲连接
  min-idle: 5  # 连接池中的最小空闲连接

6.测试

我之所以用shedlock是因为确保在集群环境下各微服务的定时任务只执行一个,而不是全部都运行相同的定时任务。

本次测试效果如下:

 

 

posted @ 2020-11-23 10:15  残冰辉  阅读(281)  评论(0)    收藏  举报