代码编织梦想

为了完成既能通过数据库修改定时任务,也能通过接口实现定时任务的功能

而且不用框架,这就要用到Spring的定时任务线程池了,

首先创建一个类创建线程池

public class OrderCollectScheduledConfig {
	/**
	 * 设置线程池,防止多个任务同步执行造成部分数据不会显示
	 * @return
	 */
	@Bean
	public TaskScheduler taskScheduler() {
		log.info("创建定时任务调度线程池 start");
		ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
		executor.setPoolSize(10);
		executor.setThreadNamePrefix("taskExecutor-");
		executor.setRemoveOnCancelPolicy(true);
		//设置饱和策略
		//CallerRunsPolicy:线程池的饱和策略之一,当线程池使用饱和后,直接使用调用者所在的线程来执行任务;如果执行程序已关闭,则会丢弃该任务
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		executor.initialize();
		log.info("创建定时任务调度线程池 end");
		return executor;
	}
}

然后生成一个实现了Runable接口的类

如下

public class OrderCollectRunable implements Runnable{
	private ScmJob scmJob;

	public OrderCollectRunable(ScmJob scmJob) {
		this.scmJob=scmJob;
	}

	@Override
	public void run() {
		try {
			Class<?> clazz = Class.forName(scmJob.getBeanName());
			Object bean = SpringContextUtils.getBean(clazz);
			Method method = ReflectionUtils.findMethod(bean.getClass(),scmJob.getMethodName(),ScmJob.class);
			ReflectionUtils.invokeMethod(method,bean,scmJob);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		OrderCollectRunable that = (OrderCollectRunable) o;
		return scmJob.equals(that.scmJob);
	}

	@Override
	public int hashCode() {
		return Objects.hash(scmJob);
	}
}

其中Run方法是你需要的业务逻辑,

然后来到核心代码:

public class CronTaskRegistrar {

	private final Map<ScmJob, ScheduledTask> scheduledFutureMap = new ConcurrentHashMap<>(16);
	public final Map<String,ScmJob> map=new HashMap();
	@Autowired
	private TaskScheduler taskScheduler;
	@Autowired
	private ThreadPoolTaskScheduler threadPoolTaskScheduler;
	@Autowired
	private ScmJobService scmJobService;


	private static final Logger LOGGER = LoggerFactory.getLogger(CronTaskRegistrar.class);

	public void start(ScmJob scmJob) {
		LOGGER.info("准备启动任务:{}", scmJob.getJobName());
		try {
			//校验是否已经启动
			if (this.isStart(scmJob)) {
				LOGGER.info("当前任务已在启动列表,请不要重复启动!");
			}else{
				//启动任务
				this.doStart(scmJob);
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 根据任务id 判断定时任务是否启动
	 */
	public Boolean isStart(ScmJob scmJob) {
		//首先检查scheduledFutureMap是否存在该任务,如果不存在,则确定当前任务并没有启动
		if (scheduledFutureMap.containsKey(scmJob)) {
			//当该任务存在时,需要检查scheduledFuture对象是否被取消,如果为false,说明当前线程在启动,否则当前线程处于关闭状态
			if (!scheduledFutureMap.get(scmJob).future.isCancelled()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 根据任务id 停止定时任务
	 * 该方法加锁,避免
	 */
	public void stop(ScmJob scmJob) {
		LOGGER.info("进入关闭定时任务 :{}", scmJob.getJobName());
		//首先检查当前任务实例是否存在
		if (scheduledFutureMap.containsKey(scmJob)) {
			try {
				//获取任务实例
				ScheduledTask scheduledTask = scheduledFutureMap.get(scmJob);
				//关闭定时任务
				scheduledTask.cancel();
				//避免内存泄露
				//scheduledFutureMap.remove(id);
				LOGGER.info("任务{}已成功关闭", scmJob.getJobName());
			}catch (Exception e) {
				e.printStackTrace();
			}
		}else {
			LOGGER.info("当前任务{}不存在,请重试!", scmJob.getJobName());
		}
	}

	public void init(ScmJob scmJob){
		LOGGER.info("定时任务开始初始化:"+scmJob.getJobName());
		//如果集合为空,则直接退出当前方法
		if (scmJob==null) {
			return;
		}
		else {
			if (!isStart(scmJob)){
				doStart(scmJob);
			}
		}

	}

	/**
	 * 启动定时任务(该方法设置为私有方法,不开放给对象直接调用)
	 */
	private void doStart(ScmJob scmJob) {
		OrderCollectRunable orderCollectRunable = new OrderCollectRunable(scmJob);
		ScheduledTask scheduledTask = new ScheduledTask();
		ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(orderCollectRunable,
				new Trigger() {
					@Override
					public Date nextExecutionTime(TriggerContext triggerContext) {
						CronTrigger cronTrigger = new CronTrigger(scmJob.getCron());
						return cronTrigger.nextExecutionTime(triggerContext);
					}
				});
		scheduledTask.future=scheduledFuture;
		//将已经启动的定时任务实例放入scheduledFutureMap进行统一管理
		scheduledFutureMap.put(scmJob, scheduledTask);
		map.put(scmJob.getJobName(),scmJob);
		LOGGER.info("启动任务:{} 成功!",scmJob.getJobName());
	}

}

通过对threadPoolTaskScheduler.schedule的操作将定时任务放到线程池中,这样就可以手动的去取消或者增加定时任务

注意这里要重写实体类的equals方法和Hashcode方法

要不然比对不上,还有map的作用是为了存储老的定时任务的实体类,方便下次做比对,下次的实体类和这次的实体类相同,则不修改定时任务

接下来就是细枝末节的东西了

public final class ScheduledTask {
	volatile ScheduledFuture<?> future;


	/**
	 * 取消定时任务
	 */
	public void cancel() {
		ScheduledFuture<?> future = this.future;
		if (future != null) {
			future.cancel(true);
		}
	}

}

@Component
public class RunConfig implements ApplicationRunner {
	@Resource
	private ScmJobService scmJobService;
	@Resource
	private CronTaskRegistrar cronTaskRegistrar;
	@Override
	public void run(ApplicationArguments args) throws Exception {
		LambdaQueryWrapper<ScmJob> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(ScmJob::getJobName,"orderCollect");
		ScmJob start = scmJobService.getOne(queryWrapper);
		if (start!=null){
			cronTaskRegistrar.start(start);
		}
		//TODO 执行线程同步操作
		ScmJob synchronization = new ScmJob();
		synchronization.setJobName("synchronization");
		synchronization.setCron("0/5 * * * * ?");
		synchronization.setBeanName("com.jxaisino.scm.controller.ScmOrderCollectController");
		synchronization.setMethodName("synchronizationOrderCollectSetting");
		cronTaskRegistrar.start(synchronization);
	}

这个类是为了项目启动时就加入同步线程

public void synchronizationOrderCollectSetting(ScmJob scmJob) {
		LambdaQueryWrapper<ScmJob> lambdaQueryWrapper = new LambdaQueryWrapper<>();
		lambdaQueryWrapper.eq(ScmJob::getJobName,"orderCollect");
		ScmJob newScmJob = this.getOne(lambdaQueryWrapper);
		CronTaskRegistrar cronTaskRegistrar = SpringContextUtils.getBean(CronTaskRegistrar.class);
		ScmJob oldScmJob = cronTaskRegistrar.map.get("orderCollect");
		if (newScmJob!=null&&!newScmJob.equals(oldScmJob)){
			if (oldScmJob!=null){
				cronTaskRegistrar.stop(oldScmJob);
			}
			cronTaskRegistrar.start(newScmJob);
		} else if (newScmJob==null&&oldScmJob!=null){
			cronTaskRegistrar.stop(oldScmJob);
			cronTaskRegistrar.map.remove("orderCollect");
		}
	}

最后是线程同步的方法,通过比对新的实体类 与老的实体类是否一致而决定如何操作

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a1257935/article/details/128014703

关于spring定时任务时间配置_weixin_42225496的博客-爱代码爱编程_spring定时任务时间配置

一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。 按顺序依次为 1.秒(0~59) 2.分钟(0~59) 3.小时(0~23) 4.天(月)(0~31,但是你需要考虑你月的天数) 5.月(0~11) 6.天(

java 定时 spring_Spring定时任务实现与配置(一)-爱代码爱编程

朋友的项目中有点问题。他那边是Spring架构的,有一个比较简单的需要定时的任务执行。在了解了他的需求之后,于是提出了比较简单的Spring+quartz的实现方式。 注意本文只是讨论,在已搭建完毕的Spring工程下,完成最简单的定时任务。 第一步,要知道Spring这个架构,很有趣很有意思。可以做到自由插拔功能模块的效果。工程项目是基于MAVE

java 异步定时任务_spring 定时任务 & 异步调用-爱代码爱编程

本篇仅限于 Spring 定时任务 & 异步调用的基本使用,不涉及深入原理探究,先学会怎么用,再探究原理. 环境:SpringBoot 2.0 定时任务指的是应用程序在指定的时间执行预先定义好的程序片段 在 Spring 中使用定时任务非常简便,分为三步:编写定时任务类并注入到 IOC 容器,一般使用 @Component 注入 编写定

【spring】两步轻松实现定时任务_spring定时任务-爱代码爱编程

【Spring】两步轻松实现定时任务 已完结 文章目录 【Spring】两步轻松实现定时任务一、两步编码实现定时任务1. 开启定时任务注解2. 设置执行时间3. cron表达式实现 二、cron表

spring的task定时任务与事务_定时任务事务问题-爱代码爱编程

目录 SpringTask定时任务 xml方式 1,加载Sring核心依赖(pom.xml配置) 2,添加配置文件,添加IOC扫描器 3,定义定时任务 4,加载定时器下xml配置 6,现象证明 注解方式 定时方法 xml配置 现象 Cron表达式 Spring事务 事务的四大特性(ACID) 原子性(Atomicity) 隔