• 一文学会线程池、任务调度的使用


    一文学会线程池、任务调度的使用

    本文主要讲解线程池以及定时任务的使用,以及在分布式环境下、JUC线程池和Spring线程池的弊端。

    image-20221113205924060

    起因:

    分布式换环境下的定时任务问题

    • ❓ 有没有可能会出现这个问题,使用JUC或者Spring线程池的话,他们只能配置间隔多长时间执行一次,因为是集群的缘故,他们重复执行,这样有意义吗?

    image-20221113210018752

    • ❓ 而Quartz定时任务驱动的参数存到数据库里,通过排队加锁等这样的机制实现共享(也就是同一时间,只有一台服务器执行)

    image-20221113210643527

    1、JUC线程池

    ExecutorService

    • ❗️ 使用步骤:

      1. Executors.newFixedThreadPool(5); 初始化线程池线程数量并构造出ExecutorService
      2. 通过Runnable接口,构造需要执行的内容
      3. 通过ExecutorServicesubmit启动任务
    • ❗️ 具体代码:

    •   private ExecutorService executorService = Executors.newFixedThreadPool(5); // 初始化线程池线程数量
        
        public void testExecutorService() {
            // 线程任务
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    log.debug("HELLO ExecutorService");
                }
            };
            for (int i = 0; i < 10; i++) {
                executorService.submit(task); // 多次执行
            }
            sleep(10); // 防止线程终止
        }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    ScheduledExecutorService

    可执行定时任务的线程池

    • ❗️ 使用步骤:
      1. Executors.newScheduledThreadPool(5) 初始化线程池线程数量并构造出ScheduledExecutorService
      2. 通过Runnable接口,构造需要执行的内容
      3. 通过ScheduledExecutorServicescheduleAtFixedRate启动任务
    • ❗️ 具体代码

    scheduledExecutorService还有需要重载的方法:

        /**
         * JDK可执行定时任务的线程池
         */
        private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
    
    	public void testScheduleExecutorService() {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    log.debug("HELLO ScheduleExecutorService");
                }
            };
            // 需要执行的任务,第一次延迟多久执行,每个多久执行一次,时间单位
            scheduledExecutorService.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);
            sleep(10);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、Spring线程池

    注意:

    • Spring线程池的使用需要创建配置文件开启任务调度才可使用,需要初始化ThreadPoolTaskScheduler(线程池任务调度器)
    • image-20221113213206870

    ThreadPoolTaskExecutor

    • ❗️ 使用步骤:
      1. application.yaml配置线程池属性 可通过TaskExecutionProperties查看更为详细的配置信息
      2. image-20221113212249574
      3. 注入ThreadPoolTaskExecutor
      4. 通过Runnable接口,构造需要执行的内容
    • ❗️ 具体代码
        @Resource
        private ThreadPoolTaskExecutor taskExecutor;
    
    	public void testThreadPoolTaskExecutor() {
            // 需要执行的任务
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    log.debug("HELLO ThreadPoolTaskExecutor");
                }
            };
            for (int i = 0; i < 10; i++) {
                taskExecutor.submit(task);
            }
            sleep(10);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    ThreadPoolTaskScheduler

    可执行定时任务的线程池

    • ❗️ 使用步骤:

      1. application.yaml配置调度属性,可通过TaskSchedulingProperties查看更为详细的配置信息
      2. image-20221113212601622
      3. 容器中注入ThreadPoolTaskScheduler
      4. 通过Runnable接口,构造需要执行的内容
    • ❗️ 具体代码

    •       // Spring定时任务线程池
            @Resource
            private ThreadPoolTaskScheduler taskScheduler;
        	@Test
        	public void testThreadPoolTaskScheduler() {
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        log.debug("HELLO ThreadPoolTaskScheduler");
                    }
                };
                // 需要执行的任务,隔多久执行一次
                taskScheduler.scheduleAtFixedRate(task, 2000);
                sleep(10);
        	}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    简化使用

    你以为不觉得这样的配置过于繁杂了吗,Spring也想到了这点,使用注解的方式来简化使用。

    image-20221113213952444

    3、Quratz

    配置

    导入依赖

    <dependency>
    	<groupId>org.springframework.bootgroupId>
    	<artifactId>spring-boot-starter-quartzartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    核心概念

    • 任务 Job

      • 我们想要调度的任务都必须实现 org.quartz.job 接口,然后实现接口中定义的 execute() 方法即可,类似于TimerTask
    • 触发器 Trigger

      • Trigger 作为执行任务的调度器。我们如果想要凌晨1点执行备份数据的任务,那么 Trigger 就会设置凌晨1点执行该任务。
      • 其中 Trigger 又分为 SimpleTriggerCronTrigger两种
    • 调度器 Scheduler

      • Scheduler为任务的调度器,它会将任务 Job 及触发器 Trigger整合起来,负责基于 Trigger 设定的时间来执行 Job

    image-20221113214533265

    使用

    使用步骤:

    1. 定义Job
    2. 实例化JobDetailSimpleTigger
    3. (需要持久化到数据库,就得配置)
    • 定义Job 也就是需要执行的内容
    @Slf4j
    public class AlphaJob implements Job {
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            log.warn(Thread.currentThread().getName()+": execute a quartz job.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 实例化JobDetailSimpleTigger
    /**
     * 启动项目,将配置信息初始化到数据库中,之后直接访问数据库,
     * 数据库可以被每个实例所获取,从而实现了分布式定时任务
     *
     * @author : look-word
     * 2022-11-13 20:11
     **/
    @Configuration
    public class QuartzConfig {
        // 配置JobDetail(任务)
        @Bean
        public JobDetailFactoryBean alphaJobDetail() {
            JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
            factoryBean.setJobClass(AlphaJob.class); // 需要被执行的任务
            factoryBean.setName("alphaJob");
            factoryBean.setGroup("alphaJobGroup");
            factoryBean.setDurability(true); // 是否持久化到数据库
            factoryBean.setRequestsRecovery(true); // 当出现问题,是否可恢复
            return factoryBean;
        }
    
        // 配置 Trigger(触发器)
        @Bean
        public SimpleTriggerFactoryBean alphaTrigger(JobDetail alphaJobDetail) {
            SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
            factoryBean.setJobDetail(alphaJobDetail);
            factoryBean.setName("alphaTrigger");
            factoryBean.setGroup("alphaTriggerGroup");
            factoryBean.setRepeatInterval(3000); // 间隔多久执行
            factoryBean.setJobDataMap(new JobDataMap());
            return factoryBean;
        }
    }
    
    • 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

    配置好上面信息,启动项目,我们的Quratz就是执行了。

    如若想要实现持久化到数据,配置下面步骤。

    持久化

    更为详细的介绍

    为什么持久化到数据中

    • 没有配置的都是存储在内存里面的。 当程序突然被中断时,如断电,内存超出时,很有可能造成任务的丢失。 可以将调度信息存储到数据库里面,进行持久化,当程序被中断后,再次启动,仍然会保留中断之前的数据,继续执行,而并不是重新开始。

    创建 持久化表

    文件地址

    持久化到数据库的配置

    # quartz 分布式定时任务
    spring:
      quartz:
        job-store-type: jdbc
        scheduler-name: communityScheduler
        properties:
          org:
            quartz:
              scheduler:
                instanceId: AUTO  #调度器id自动生成
              jobStore:
                class: org.quartz.impl.jdbcjobstore.JobStoreTX                      # 完成Job持久化配置的类
                driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate   # 存储使用的JDBC
                isClustered: true                                                   # 是否以集群的方式
              threadPool:
                class: org.quartz.simpl.SimpleThreadPool                            # 使用的线程类
                threadCount: 5														# 线程池的数量
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 然后再次启动项目,我们对于Quartz的配置,就会持久化到数据库中。

    当然,我们的任务持久到数据库中,假如不需要了呢,不可能一个个的去删除吧,那么Quratz也为我们提供了具体的方法。

    • JobKey(配置的任务名称,配置的组名称)
    @Resource
    private Scheduler scheduler;
    
    @Test
    public void testDeleteJob() throws SchedulerException {
        System.out.println(scheduler.deleteJob(new JobKey("alphaJob", "alphaJobGroup")));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    执行后,会发现持久化到数据库中的信息,被删除了。

  • 相关阅读:
    phpstudy RCE脚本编写(Python)
    8+非肿瘤+线粒体+实验生信思路解析
    U3D游戏开发中使用框架与UGUI+JSON配置文件的代码思路
    SQL语言的分类:DDL(数据库、表的增、删、改)、DML(数据的增、删、改)
    探索服务器潜能:创意项目、在线社区与其他应用
    随笔 | 写在十一月的第一天
    DevOps和SRE还没搞清楚,平台工程又出现了,它会取代DevOps吗?
    用原生JS实现虚表控件
    美食节目:视觉盛宴如何唤醒沉睡的食欲
    独立于 Github,更方便地管理自己的静态网站?来试试这套自托管 Git 仓库方案!
  • 原文地址:https://blog.csdn.net/qq_50975965/article/details/127838749