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


    分布式调度框架

    分布式任务调度框架有:cronsun、Elastic-job、saturn、lts、TBSchedule、xxl-job 等。
    在这里插入图片描述

    方案一: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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述
    在这里插入图片描述

    方案二: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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述
    在这里插入图片描述

    方案三:SpringTask

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

    标准格式:A B C D E F G
    A表示秒;B表示分;C表示小时;D表示日;E表示月;F表示星期;G表示年
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    spring3.0以后自带的task

    注解方式:

    
      <task:annotation-driven/>
    
    • 1
    • 2
    @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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    配置方式:

     
        <task:scheduled-tasks>
        	
            <task:scheduled ref="job1" method="run" fixed-rate="5000"/>
        task:scheduled-tasks>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    每5秒执行一次

    public class job1 {
        public void run(){
            System.out.println("job1....."+new Date());
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    整合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);
    
    			//核实订单状态,调用微信支付
    			
    		}		
    	}
    
    • 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
    @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);
    	}
    	
    }
    
    • 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
    • 43

    定时任务+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,"");
    		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    //对消息的监听,进行接收消息
    @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();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    //业务接口以及实现
    @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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    方案四:多线程执行

    基于注解设置多线程

    @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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    五种多线程方法提高效率

    • 线程(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);
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    //创建三个实例
    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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    a 和 b 同时开始运行,轮流输出。a 完成后,c 开始执行。此外,它们全部在不同的线程中运行。通过手动创建 Thread 实例,您可以完全控制它们。
    在这里插入图片描述

    在这里插入图片描述

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

    private static List<BurreredImage> resizeAll(List<BufferedImage> sourceImages,int width,int height){
    	
    	return sourceImages
    		.parallelStream()
    		.map(source -> resize(source,width,height))
    		.toList();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    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);
                }
            }
        }
    }
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    请注意,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("图像调整大小成功!");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    借助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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    方案五:quartz

    基于时间间隔的定时任务
    基于Cron表达式的定时任务
    Job具体的工作内容/Trigger触发器,指定运行参数(运行次数、开始时间、时长)/Scheduler将job和Trigger关联起来
    多触发器的定时任务、Job中注入Bean、Quartz持久化
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    一个JobDetail(Job的实现类)可以绑定多个Trigger,但一个Trigger只能绑定一个JobDetail
    每个JobDetail和Trigger通过group和name来标识唯一性;
    一个Scheduler可以调度多组JobDetail和Trigger。
    在这里插入图片描述

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-quartzartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    @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();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    只要启动SpringBoot羡慕就会输出:
    在这里插入图片描述

    配置的方式:

      <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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    	
        <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>
    
    
    • 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
    public class QuartzJob{
    	public void print(){
    		System.out.println("quartzjob.........."+new Date());
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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()));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建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();
    	}
    }
    
    • 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

    启动测试方法后,控制台观察现象即可。注意到这么一句日志: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);
    	}
    }
    
    • 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

    基于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();
        }
    }
    
    • 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

    整个Quartz设计的类之间的关系
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    CentOS 7使用RPM包安装MySQL5.7
    [附源码]Python计算机毕业设计Django仓储综合管理系统
    【论文写作】符号:矩阵、向量的乘法、内积、点积等
    10月 kaggle-酶稳定性预测(群链接)
    plsql 查询数据中文乱码
    【LeetCode热题100】打开第5天:最长回文子串
    jupyter lab常用插件集合
    二阶贝塞尔曲线模拟人拖动鼠标
    供暖系统如何实现数据远程采集?贝锐蒲公英高效实现智慧运维
    IDEA 连接 数据库
  • 原文地址:https://blog.csdn.net/usa_washington/article/details/132723136