SpringBoot定时器(Spring Schedule)

定时器可以按照设定的时间自动重复执行某一个动作。比如用户下订单,现在希望将30分钟内未付款的订单自动取消,就可以写一个定时器,让它每多长时间(如一分钟,两分钟,在这种严格要求数据具有实时性的场景下,这个时间在保证任务能执行完的情况下肯定越小越精确)执行一次,执行的时候,遍历数据库订单表,拿当前时间减去订单创建时间,超过30分钟的删除掉。

一、SpringBoot集成定时器:

先搭建好一个springBoot工程,

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.demo</groupId>
	<artifactId>job</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.4.1.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
</project>

有启动类:


package com.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
//启动定时
@EnableScheduling
public class JobStart {
	public static void main(String[] args) {
		SpringApplication.run(JobStart.class, args);
	}
}

定时Job:


package com.demo.job;
import java.util.Date;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
//或者其他注解都可以,只要让这个类可以被扫描到
@Component       
public class JobDemo {
	
	@Scheduled(cron = "0/30 * * * * ?")
	public void myTest1(){
		System.out.println("job1每30秒执行一次"+new Date());
	}
	


	@Scheduled(cron = "0 0/1 * * * ? ")
	public void myTest2(){
		System.out.println("job2每一分钟执行一次"+new Date());
	}

}

之后启动JobStart类启动整个工程,可以看到控制台打印信息如下:

完成。

二、简单应用举例: 

例1,现在有一个抢购活动,活动有三种状态:在开始时间(beginTime)前的为未开始状态,在开始时间和结束时间endTime之间的为进行中状态,在结束时间后的为结束状态。创建活动时会设定开始时间、结束时间、状态(均为未开始),现在通过一个job来自动改变活动的状态:

@Component
public class ActivityTask{

@Autowired
private GroupActivityService groupActivityService;

@Scheduled(cron = "0 0/1 * * * ? ")
public void activity() {
		try {
			
			//遍历活动表,获取所有未结束的活动
			List<Activity> activityList = groupActivityService.getNotEndActivityList();		
			for(Activity activity:activityList){
				Short status = activity.getStatus();
				Date beginTime = activity.getBeginTime();
				Date endTime = activity.getEndTime();
				Date date = new Date();
				//待开始的,判断是否开始了
				if(status == (short)ActivityStatusEnum.WAIT.getCode()){
					
					//当前时间>开始时间  && 当前时间<结束时间,开始
					if(date.after(beginTime)  &&  endTime.after(date)){
						activity.setStatus((short)ActivityStatusEnum.START.getCode());
						groupActivityService.update(activity);
					}
				}
				
				
				//开始的,判断是否结束了
				if(status == (short)ActivityStatusEnum.START.getCode()){
					//当前时间>结束时间,结束
					if(date.after(endTime)){
						activity.setStatus((short)ActivityStatusEnum.END.getCode());
						groupActivityService.update(activity);
					}
				}
				
			}
		} catch (Throwable t) {
			logger.error(t.toString());
		}
		logger.debug("end job" + new Date());
	}
}

例2:定时清理数据,自动转移一年前的历史数据,一天执行一次,一次转移10条(从数据库性能方面考虑一次性全部转移比较慢)

//自动转移一年前的历史数据
@Scheduled(cron = "0 0 3 * * ?")
	//@Scheduled(cron = "0 0/1 * * * ? ")
	public void tranferToHistory() {
		
		logger.debug("tranferToHistory job begin: " + new Date());
		try {
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(new Date());
			calendar.add(Calendar.YEAR, -1);
			Date beforeTime = calendar.getTime();
			userService.transferToHistory(beforeTime, 10);
		} catch (Exception e) {
			logger.error("tranferToHistory job error: " + e.getMessage());
		}
		logger.debug("tranferToHistory job end: " + new Date());
	}

userServiceImpl:

@Override
	@Transactional
	public int transferToHistory(Date beforeTime, Integer size) {
		//查询beforeTime之前的size条数据
		List<User> userList  = userDao.selectHistoryUser(beforeTime,size);
		List<Long> userIds = new ArrayList<>();
		for(User user:userList){
			userIds.add(user.getId());
		}
		if(!userIds .isEmpty()){
			//1、转移用户表数据t_user至t_user_history
			notifyDao.insertToUserHistory(userList);	
			notifyDao.deletesUserForever(userIds);
			//2、转移评论t_user_comment中的老数据至t_user_comment_history表
			List<UserComment> commentList = 
           userCommentDao.selectHistoryComment(userIds);
			userCommentDao.insertCommentHistory(commentList);
			userCommentDao.deletesCommentForever(userIds);
          //......转移和用户相关的其他老数据
			
		}
		
		return 1;
	}

注:在线cron表达式生成器:http://cron.qqe2.com/

三、高阶应用:

1、schedule是默认以单线程的情况执行的:

如:


package com.demo.job;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

 /** 
 * @Description: 单线程举例
 */
@Component
public class ThreadJobDemo {
	
	@Scheduled(cron = "0/30 * * * * ?")
	public void myTest1(){
		System.out.println("job1每30秒执行一次,线程:"+Thread.currentThread().getId());
	}
	
 
 
	@Scheduled(cron = "0 0/1 * * * ? ")
	public void myTest2(){
		System.out.println("job2每一分钟执行一次,线程:"+Thread.currentThread().getId());
	}
}

启动类:

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

 启动后,控制台打印,可以看到线程id都是一样的,说明是在以同一个线程执行:

 

2、使用 @Async 注解可以实现多线程,启动类上需要加@EnableAsync 注解:这一点从百度上看到的,我实验了结果不正确。

 

 

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页