五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)

分布式调度框架

分布式任务调度框架有:cronsun、Elastic-job、saturn、lts、TBSchedule、xxl-job 等。
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第1张图片

方案一:Timer

(1)Timer.schedule(TimerTask task,Date time)安排在制定的时间执行指定的任务。

(2)Timer.schedule(TimerTask task,Date firstTime ,long period)安排指定的任务在指定的时间开始进行重复的固定延迟执行.

(3)Timer.schedule(TimerTask task,long delay)安排在指定延迟后执行指定的任务.

(4)Timer.schedule(TimerTask task,long delay,long period)安排指定的任务从指定的延迟后开始进行重复的固定延迟执行.

(5)Timer.scheduleAtFixedRate(TimerTask task,Date firstTime,long period)安排指定的任务在指定的时间开始进行重复的固定速率执行.

(6)Timer.scheduleAtFixedRate(TimerTask task,long delay,long period)安排指定的任务在指定的延迟后开始进行重复的固定速率执行.

public class TestTimer{
	
	public static void main(String[] args){
		
		TimerTask timerTask = new TimerTask(){
			@Override
			public void run(){
				System.out.println("tesk run:" + new Date());
			}
		};

		Timer timer = new Timer();

		//安排指定的任务在指定的时间开始进行重复的而固定延迟执行,每3庙执行一次
		timer.schedule(timeTask,10,3000);
	}
}

五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第2张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第3张图片

方案二:ScheduleExecutorService

public class TestScheduleExecutorService{
	
	public static void main(String[] args){
		
		ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

		/**
			参数:任务体、首次执行的延时时间、任务执行间隔、间隔时间单位
		*/
		service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()),
									 0, 3, TimeUnit.SECONDS);
	}
}

五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第4张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第5张图片

方案三:SpringTask

Cron表达式:若干数字、空格、符号按照一定的规则组成一组字符串来表达时间信息。在线corn表达式生成器:https://www.pppet.net/

标准格式:A B C D E F G
A表示秒;B表示分;C表示小时;D表示日;E表示月;F表示星期;G表示年
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第6张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第7张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第8张图片

spring3.0以后自带的task

注解方式:


  <task:annotation-driven/>
@Slf4j
@Component
public class ScheduledService{
	
	@Scheduled(cron = "0/5 * * * * *")
	public void scheduled(){
		log.info("====>使用cron {}",System.currentTimeMillis());
	}

	@Scheduled(fixedRate = 5000) //每5秒运行一次
	public void scheduled1(){
		log.info("=====>>>>>使用fixedRate{}", System.currentTimeMillis());
	}

	@Scheduled(fixedDelay = 5000)
    public void scheduled2() {
        log.info("=====>>>>>fixedDelay{}",System.currentTimeMillis());
    }
}

五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第9张图片

配置方式:

 
    <task:scheduled-tasks>
    	
        <task:scheduled ref="job1" method="run" fixed-rate="5000"/>
    task:scheduled-tasks>

每5秒执行一次

public class job1 {
    public void run(){
        System.out.println("job1....."+new Date());
    }
}


整合SpringBoot

①、启动类添加注解,开启定时任务@EnableScheduling
②、方法上添加注解表达式

@Slf4j
@Component //项目启动的时候能够被初始化出来
public class WxPayTask{

	/**
		SpringTask引入的linux的cron表达式
		秒 分 时 日 月 周
		日和周是互斥的,指定其中之一,另一个为?
		* 每秒都执行
		1-3 从第0秒开始,每个3秒执行一次
		0/3 从第0秒开始,每隔3秒执行1次
		1,2,3 在指定的第1,2,3秒执行
	*/
	@Scheduled(cron = "* * * * * ?")//每月每天没时每分每秒执行
	public void task1(){
		log.info("task1被执行");
	}

	//从第0秒开始,每隔30秒执行1次,查询创建超过5分钟,并且未支付的订单
	@Scheduled(cron = "0/30 * * * * ?")
	public void orderConfirm(){
		
		log.info("orderConfirm 被执行……");
		//查询超过5分钟的订单
		List<OrderInfo> orderInfoList = orderInfoService.getNoPayOderByDuration(5);

		for(OrderInfo orderInfo:orderInfoList){
			String orderNo = orderInfo.getOrderNo();
			log.warn("超时订单==={}",orderNo);

			//核实订单状态,调用微信支付
			
		}		
	}
@Override
public List<OrderInfo> getNoPayOrderByDuration(int minutes){

	Instant instant = Instant.now().minus(Duration.ofMinutes(minutes));//
	
	//构造查询条件(超过5分钟+未支付)
	QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
	queryWrapper.eq("order_status",OrderStatus.NOTPAY.getType());
	queryWrapper.le("create_time",instant);

	List<OrderInfo> orderInfoList = baseMapper.selectList(queryWrapper);
	return orderInfoList;
}

//根据订单号查询微信支付查单接口
//如果订单已支付,则更新商户端订单状态,如果订单未支付,则调用关单接口关闭订单,并更新商户端订单状态
@Override
public void checkOrderStatus(String orderNo){
	
	log.warn("根据订单号核实订单状态===>",orderNo);

	//调用微信支付接口
	String result = this.queryOrder(orderNo);
	//利用工具将json转换map
	Gson gson = new Gson();
	Map resultMap = gson.fromJson(result,HashMap.class);
	
	//获取订单状态
	Object tradeState = resultMap.get("trade+state");
	//判断订单状态
	if(WxTradeState.SUCCESS.getType().equals(tradeState)){
		log.warn("核实订单已支付===>{}",orderNo);
		//如果确认订单已支付则更新本地订单状态
		orderInfoService.updateStatusByOrderNo(orderNo,OrderStatus.SUCCESS);
		//记录支付日志
		paymentInfoService.createPaymentInfo(result);
	}
	if(WxTrade.NOPAY.getType().equals(tradeState)){
		log.warn("核实订单未支付===>",orderNo);
		this.closeOrder(orderNo);
	}
	
}

定时任务+RabbitMQ

@Component
@EnableScheduling
public class ScheduleTask{
	
	@Autowired
	private RabbitService rabbitService;
	
	//每天8点执行
	@Schedule(cron = "0 0 8 * * ?")
	public void taskPatient(){
		//通过mq发送消息
		rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_TASK,MqConst.ROUTING_TASK_8,"");
		
	}
}
//对消息的监听,进行接收消息
@Componet
public class OrderReceiver{

	@Autowired
	private OrderService orderService;
	
	@RabbitListener(bindings = @QueueBinding(
		value=@Queue(value=MqConst.QUEUE_TASK_8,durable="true"),
		exchange=@Exchange(value=MqConst.EXCHANE_DIRECT_TASK),
		key={MqConst.ROUTING_TASK_8}
	))
	public void patientTips(Message message,Channel channel) throws IOException{
		orderService.patientTips();
	}
}
//业务接口以及实现
@Override
public void patientTips(){
	QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
	wrapper.eq("reserve_data",new DateTime().toString("yyyy-MM-dd"));
	wrapper.ne("order_status",OrderStatusEnum.CANCLE.getStatus());
	List<OrderInfo> orderInfoList = baseMapper.selectList(wrapper);

	for(OrderInfo orderInfo:orderInfoList){
		MsmVo msmVo = new MsmVo();
		msmVo.setPhone(orderInfo.getPatientPhone());
		String reserveData = new DateTime(orderInfo.getReserveDate()).toString();
		Map<String,Object> param = new HashMap<String,Object>(){
			{
				put("title",orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
				put("reserveData",reserveDate);
				put("name",orderInfo.getPatientName());
			}
		}

		rebbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM,MqConst.ROUTING_MSM_ITEM,msmVo);
	}
}

方案四:多线程执行

基于注解设置多线程

@Component
@EnableScheduling //开启定时任务
@EnableAsync //开启多线程
public class MultithreadScheduleTask{
	
	@Async
	@Scheduled(fixedDDelay = 5000) //间隔5秒
	public void first(){
		System.out.println("第一个定时任务开始:" + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
		System.out.println();
		Thread.sleep(1000 * 10);
	}

	 @Async
    @Scheduled(fixedDelay = 5000)
    public void second() {
        System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
        System.out.println();
    }
}

五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第10张图片

五种多线程方法提高效率

  • 线程(Thread)
  • 并行流(Parallel Streams)
  • ExecutorService
  • ForkJoinPool
  • CompletableFuture

线程类(Thread),直接控制线程的创建和管理

public class CustomTask implements Runnable{
	
	private final String name;
	private final int count;

	CustomTask(String name,int count){
		this.name = name;
		this.count = count;
	}

	@Override
	public void run(){
		//每隔50毫秒从0数到count - 1
		for(int i = 0;i < count;i++){
			System.out.println(name + "" + i + "" + Thread.currentThread().getName());

			try{
				Thread.sleep(50);
			}catch(InterruptedException e){
				throw new RuntimeException(e);
			}
		}
	}
}
//创建三个实例
Thread a = new Thread(new CustomTask("a",5));
Thread b = new Thread(new CustomTask("b",10));//b 预计计数的次数是其他实例的两倍
Thread c = new Thread(new CustomTask("c",5));

// 首先启动 a 和 b。
a.start();
b.start();

//  a 完成后开始 c。
a.join();
c.start();

a 和 b 同时开始运行,轮流输出。a 完成后,c 开始执行。此外,它们全部在不同的线程中运行。通过手动创建 Thread 实例,您可以完全控制它们。
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第11张图片

五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第12张图片

并行流(Parallel Streams),需要对大型集合中的所有元素应用相同、重复且独立的任务时,并行流非常有效
例如,图像调整大小是一个需要按顺序运行的繁重任务;当您有多个图像需要调整大小时,如果按顺序执行,将需要很长时间才能完成。在这种情况下,您可以使用并行流并行调整它们的大小

private static List<BurreredImage> resizeAll(List<BufferedImage> sourceImages,int width,int height){
	
	return sourceImages
		.parallelStream()
		.map(source -> resize(source,width,height))
		.toList();
}

ExecutorService,当实现不需要精确的线程控制时,可以考虑使用 ExecutorService。
ExecutorService 提供了更高层次的线程管理抽象,包括线程池、任务调度和资源管理
大量的异步任务堆积在一起,但是同时运行所有任务——每个任务占用一个线程——似乎太多了。线程池可以通过限制最大线程数来帮助
使用 Executors.newFixedThreadPool() 实例化 ExecutorService 来使用 3 个线程运行 10 个任务。每个任务只打印一行。请注意,我们在之前的部分中重用了之前定义的 CustomTask

ExecutorService executorService = Executors.newFixedThreadPool(3);

for(int i = 0;i < 10;i++){
	executorService.submit(new CustomTask(String.valueOf(i),1));
}
executorService.shutdown();

五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第13张图片
10 个任务在 3 个线程中运行。通过限制特定任务使用的线程数,您可以根据优先级分配线程数:对于重要且频繁的任务使用更多线程,对于琐碎或偶尔的任务使用较少线程。ExecutorService 具有高效和简洁的特点,是大多数多线程场景的首选选项。

如果您需要更多的控制和灵活性,请查看 ThreadPoolExecutor,它是 Executors.newFixedThreadPool() 返回的 ExecutorService 的实际实现。您可以直接创建其实例或将返回的 ExecutorService 实例转换为 ThreadPoolExecutor 实例以获得更多控制权。

ForkJoinPool,是另一种线程池。其中一个任务是图像调整大小。图像调整大小是分而治之问题的一个很好的例子。使用ForkJoinPool,您可以将图像分成两个或四个较小的图像,并同时调整它们的大小。以下是ImageResizeAction的示例,它将图像调整为给定的大小。

public class ImageResizeAction extends RecursizeAction{
	
	private static final int THRESHOLD = 100;

	private final BufferedImage sourceImage;
	private final BufferedImage targetImage;
	private final int startRow;
	private final int endRow;
	private final int targetWidth;
	private final int targetHeight;

	public ImageResizeAction(BufferedImage sourceImage,
                             BufferedImage targetImage,
                             int startRow, int endRow,
                             int targetWidth, int targetHeight) {
        this.sourceImage = sourceImage;
        this.targetImage = targetImage;
        this.startRow = startRow;
        this.endRow = endRow;
        this.targetWidth = targetWidth;
        this.targetHeight = targetHeight;
    }

	@Override
	protected void compute(){
		if(endRow - startRow <= THRESHOLD){
			resizeImage();
		}else{
			int midRow = startRow + (endRow - startRow) / 2;
            invokeAll(
                    new ImageResizeAction(sourceImage, targetImage,
                            startRow, midRow, targetWidth, targetHeight),
                    new ImageResizeAction(sourceImage, targetImage,
                            midRow, endRow, targetWidth, targetHeight)
            );
		}
	}

	private void resizeImage() {
        int sourceWidth = sourceImage.getWidth();
        double xScale = (double) targetWidth / sourceWidth;
        double yScale = (double) targetHeight / sourceImage.getHeight();

        for (int y = startRow; y < endRow; y++) {
            for (int x = 0; x < sourceWidth; x++) {
                int targetX = (int) (x * xScale);
                int targetY = (int) (y * yScale);
                int rgb = sourceImage.getRGB(x, y);
                targetImage.setRGB(targetX, targetY, rgb);
            }
        }
    }
}

请注意,ImageResizeAction继承了RecursiveAction。RecursiveAction用于定义递归的调整大小操作。在此示例中,图像被分成两半并并行调整大小。

您可以使用以下代码运行ImageResizeAction:

public static void main(String[] args) throws IOException {
    String sourceImagePath = "source_image.jpg";
    String targetImagePath = "target_image.png";
    int targetWidth = 300;
    int targetHeight = 100;

    BufferedImage sourceImage = ImageIO.read(new File(sourceImagePath));
    BufferedImage targetImage = new BufferedImage(targetWidth, targetHeight,
            BufferedImage.TYPE_INT_RGB);

    ForkJoinPool forkJoinPool = new ForkJoinPool();
    forkJoinPool.invoke(new ImageResizeAction(sourceImage, targetImage,
            0, sourceImage.getHeight(), targetWidth, targetHeight));

    ImageIO.write(targetImage, "png", new File(targetImagePath));

    System.out.println("图像调整大小成功!");
}

借助ForkJoinPool的帮助,您现在能够更高效地调整图像的大小,具有更好的可伸缩性,并最大程度地利用资源

CompletableFuture,能够链式地连接异步操作,使您能够构建复杂的异步管道。

public static void main(String[] args) {
    CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread().getName());
        return "Hyuni Kim";
    }).thenApply((data) -> {
        System.out.println(Thread.currentThread().getName());
        return "我的名字是" + data;
    }).thenAccept((data) -> {
        System.out.println(Thread.currentThread().getName());
        System.out.println("结果:" + data);
    });

    future.join();
}

链式操作。通过CompletableFuture.supplyAsync(),首先创建并运行一个返回字符串结果的CompletableFuture。thenApply()接受前一个任务的结果,并执行其他操作,本例中是添加一个字符串。最后,thenAccept()打印生成的数据。结果如下所示:
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第14张图片
有3个任务没有在主线程中运行,这表明它们与主逻辑并行运行。当您有具有结果并需要链接的任务时,CompletableFuture将是一个很好的选择。

方案五:quartz

基于时间间隔的定时任务
基于Cron表达式的定时任务
Job具体的工作内容/Trigger触发器,指定运行参数(运行次数、开始时间、时长)/Scheduler将job和Trigger关联起来
多触发器的定时任务、Job中注入Bean、Quartz持久化
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第15张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第16张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第17张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第18张图片

一个JobDetail(Job的实现类)可以绑定多个Trigger,但一个Trigger只能绑定一个JobDetail
每个JobDetail和Trigger通过group和name来标识唯一性;
一个Scheduler可以调度多组JobDetail和Trigger。
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第19张图片

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-quartzartifactId>
dependency>
@Configuration
public class QuartzConfig{
	
	@Bean
	public JobDetail teatQuaritzDetail(){
		return JobBuilder.newJob(MyAuartz.class).withIdentity("myQuartz").storeDurably().build();
	}

	@Bean
	public Trigger testQuartzTrigger(){
		SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
												.withIntervalInSeconds(10) //设置时间周期单位
												.repeatForever();
		return TriggerBuilder.newTrigger().forJob(testQuartzDetail())
										  .withIdentity("testQuartz")
										  .withSchedule(scheduleBuilder)
										  .build();
	}
}

只要启动SpringBoot羡慕就会输出:
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第20张图片

配置的方式:

  <dependency>
      <groupId>org.quartz-schedulergroupId>
      <artifactId>quartzartifactId>
      <version>2.3.2version>
 dependency>
 <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-context-supportartifactId>
      <version>5.3.2version>
  dependency>

	
    <bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="10" />
        <property name="maxPoolSize" value="100" />
        <property name="queueCapacity" value="500" />
    bean>
    
    <bean id="quartzJob" class="com.wanmait.mavendemo.job.QuartzJob" />
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="quartzJob" />
        <property name="targetMethod" value="print" />
    bean>



    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean" >
        <property name="jobDetail" ref="jobDetail" />
        <property name="cronExpression" value="0/5 * * * * ?" />
    bean>
    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="jobDetail">property>
        <property name="startDelay" value="0" />
        
        <property name="repeatInterval" value="2000" />
        
    bean>
    
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                
                <ref bean="cronTrigger"/>
            list>
        property>
        <property name="taskExecutor" ref="executor" />
    bean>

public class QuartzJob{
	public void print(){
		System.out.println("quartzjob.........."+new Date());
	}
}

SpringBoot定时任务:
在启动类上加@EnableScheduling开启定时任务
写一个任务类添加@Component注解被spring管理,在方法上添加@Scheduled(fixedRate = 2000)或者 @Scheduled(cron = “0-5 * * ? * *”)

案例二:

导入依赖spring-boot-starter-quartz
新建Job,实现定时执行的任务

public class SimpleJob implements Job{
	
	@Override
	public void execute(JobExecutionContext jobExecutionContext){
		//创建一个事件
		System.out.println(Thread.currentThread().getName() + "--"
                + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
	}
}

创建Scheduler和Trigger,执行定时任务

public class SimpleQuartzTest{
	
	/**
		基于时间间隔的定时任务
	*/
	@Test
	public void simpleTest() throws SchedulerException, InterruptedException{
		
		//1、创建Scheduler调度器
		SchedulerFactory schedulerFactory = new StdSchedulerFactory();
		Scheduler scheduler = schedulerFactory.getScheduler();

		//2.创建JobDetail实例,并于SimpleJob类绑定(Job执行内容)
		JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
			.withIdentity("job1","group1")
			.build();

		//3.构建Trigger(触发器),定义执行频率和时长
		Trigger trigger = TriggerBuilder.newTrigger()
			//指定group和name,这是以为身份标识
			.withIdentity("trigger-1","trigger-group")
			.startNow()//立即生效
			.withSchedule(SimpleScheduleBuilder.simpleSchedule()
			.withIntervalInSeconds(2)//每个2s执行一次
			.repeatForever())//永久执行
			.build();

		//4.将job和Trigger交给Scheduler调度
		scheduler.scheduleJob(jobDetail,trigger);
		//5.启动Scheduler
		scheduler.start();
		//休眠,决定调度器运行时间,这里设置30s
		TimeUnit.SECONDS.sleep(30);
		//关闭scheduler
		scheduler.shutdown();
	}
}

启动测试方法后,控制台观察现象即可。注意到这么一句日志:Using thread pool ‘org.quartz.simpl.SimpleThreadPool’ - with 10 threads.,这说明Scheduler确实是内置了10个线程的线程池,通过打印线程名也印证了这一点。
通过TimeUnit.SECONDS.sleep(30);设置休眠,是因为定时任务是交由线程池异步执行的,而测试方法运行结束,主线程随之结束导致定时任务也不再执行了,所以需要设置休眠hold住主线程。在真实项目中,项目的进程是一直存活的,因此不需要设置休眠时间。

动态调整定时任务执行corn表达式、动态关闭开启定时任务

@Configuration
public class ScheduledConfig implements SchedulingConfigurer{
	
	@Autowired
	private ApplicationContext context;

	@Override
	public void confugureTasks(ScheduledTaskRegistrar taskRegistrar){
		
		for(SpringScheuledCron springScheduledCron:cronRepository.findAll()){
			
			Class<?> clazz;
			Object task;

			try{
				clazz = Class.forName(springScheduledCron.getCronKey());
				task = context.getBean(clazz);
			}catch(ClassNotFoundException e){
				throw new IllegalArgumentException("spring_scheduled_cron表数据" + springScheduledCron.getCronKey() + "有误", e);
			}catch(BeansException e){
				throw new IllegalArgumentException(springScheduledCron.getCronKey() + "未纳入到spring管理", e);
			}

			Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口");
		
			//可以通过改变书库数据进而实现动态改变执行周期
			taskRegistrar.addTriggerTask(((Runnable)task),
				triggerContext -> {
					//可以使用持久层,比如Mybatis来实现,从数据库中获取
					String cronExpression = "0/10 * * * * ?";
					 return new CronTrigger(cronExpression).nextExecutionTime(triggerContext);
				}
			);
		}
	}

	@Bean
	public Executor taskExecutor(){
		return Executors.newScheduledThreadPool(10);
	}
}

基于Cron表达式的定时任务

public class SimpleQuartzTest {

    /*
     * 基于cron表达式的定时任务
     */
    @Test
    public void cronTest() throws SchedulerException, InterruptedException {
        // 1、创建Scheduler(调度器)
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2、创建JobDetail实例,并与SimpleJob类绑定
        JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
                .withIdentity("job-1", "job-group").build();
        // 3、构建Trigger(触发器),定义执行频率和时长
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger-1", "trigger-group")
                .startNow()  //立即生效
                .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 *"))
                .build();

        //4、执行
        scheduler.scheduleJob(jobDetail, cronTrigger);
        scheduler.start();
        // 休眠,决定调度器运行时间,这里设置30s
        TimeUnit.SECONDS.sleep(30);
        // 关闭Scheduler
        scheduler.shutdown();
    }
}

整个Quartz设计的类之间的关系
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第21张图片
五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)_第22张图片

你可能感兴趣的:(spring,java,定时任务方案)