7.13任何执行和调度

每隔一个小时算帖子的分数,每隔半小时清理服务器的临时文件,这些需求都需要用到任务调度组件来解决。

任务调度组件是基于多线程的,但凡用多线程,一定会用到线程池,因为创建线程是有开销的,且开销较大。使用线程池来管理线程,能够让线程复用,提高处理能力,节约资源。

线程池也是重点。

在这里插入图片描述
jdk线程池是jdk自带的,spring线程池是spring自带的, 其中executor services和thread pool task executor是普通线程池
在这里插入图片描述
scheduled executor services以及 thread pool task scheduler所创建的线程可以执行定时任务。但是分布式的时候,这俩是有问题的,scheduler不合适,因为scheduler程序运行所依赖的参数都是存在数据库中的,scheduler没有解决分布式的问题。
比如,每隔十分钟,删掉一个临时文件,两个都会这样做,会产生一定冲突
jdk和spring的定时任务组件,都是基于内存的,配置多长时间运行一次,配置参数都在内存中,但是服务器1和2内存不共享,不能知道各自正在干嘛,因此会产生一定冲突

因此分布式下用Quartz更多

Quartz程序运行所依赖的参数都是存在数据库(DB)里面的,因此不管部署多少个Quartz,都会访问同一个数据库。(服务器可以有多个,但是数据库只有一个)
在这里插入图片描述
浏览器把请求发给Nginx,Nginx依据一定策略,选择服务器对请求进行处理,如果是普通请求,则是分配给controller处理

如果改为Quartz,可以对不同的请求进行排队操作。
在这里插入图片描述
我们最终的目的是,使用Quartz,但是在这之前,应该先学会用 jdk和spring线程池。

接下来创建test
ThreadPoolTests

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class ThreadPoolTests {

    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolTests.class);//logger在输出内容的时候,自然会带上线程的id以及时间

    // JDK普通线程池
    private ExecutorService executorService = Executors.newFixedThreadPool(5);//通过工厂Executors来实例化,实例化后包含五个线程,反复复用这五个线程

    // JDK可执行定时任务的线程池
    private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

    // Spring普通线程池
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    // Spring可执行定时任务的线程池
    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;

    @Autowired
    private AlphaService alphaService;

    //ThreadPoolTests是junit测试方法,其和main方法不一样,如果在main方法里启动线程,该线程不挂掉的话,main会挡着ThreadPoolTests执行,使得main会挡着ThreadPoolTests执行,不会立刻结束。
    //但是junit测试方法 每启动一个子线程,和当前线程是并发的,如果test方法没有逻辑,立刻就结束了,不管启动的线程有没有完成。
    //因此当test方法启动完一个线程以后,等其执行完以后在关闭线程。则让当前主线程sleep(阻塞)一会儿
    private void sleep(long m) {//m单位是毫秒
        try {
            Thread.sleep(m);//当前线程阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 1.JDK普通线程池
    @Test
    public void testExecutorService() {//需要给线程池一个任务 来使得线程池 分配一个线程去执行。任务即是线程体。
        Runnable task = new Runnable() {
            @Override
            public void run() {
                logger.debug("Hello ExecutorService");
            }
        };

        for (int i = 0; i < 10; i++) {//执行10次
            executorService.submit(task);//每调用一个submit方法,线程池都会给其分配一个线程以执行线程体。
        }

        sleep(10000);//不然,线程还没执行完,方法就结束了。1w毫秒就是10s
    }

结果:
1-5个线程来回使用
在这里插入图片描述
输出内容:
在这里插入图片描述

// 2.JDK定时任务线程池(设置时间间隔不断执行,要提供一个线程体)
@Test
public void testScheduledExecutorService() {
    Runnable task = new Runnable() {
        @Override
        public void run() {
            logger.debug("Hello ScheduledExecutorService");
        }
    };

    scheduledExecutorService.scheduleAtFixedRate(task, 10000, 1000, TimeUnit.MILLISECONDS);//第一个参数是任务。第二个参数是该任务延迟多少ms才执行。第三个参数是时间间隔ms,第三个参数是声明时间单位TimeUnit.MILLISECONDS

    sleep(30000);
}

在这里插入图片描述
在application properties中增加

# TaskExecutionProperties spring普通线程池的配置
# 线程池中有几个核心线程
spring.task.execution.pool.core-size=5
# 当核心线程不够用,最多扩展到15个
spring.task.execution.pool.max-size=15
#queue-capacity指队列容量,当15个线程还是不够用,需要将其放在队列中等候。设置队列能缓冲一百个任务
spring.task.execution.pool.queue-capacity=100

# TaskSchedulingProperties spring能启动定时任务的线程池的配置
spring.task.scheduling.pool.size=5
// Spring普通线程池
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
  // 3.Spring普通线程池
    @Test
    public void testThreadPoolTaskExecutor() {
        Runnable task = new Runnable() {//声明线程体
            @Override
            public void run() {
                logger.debug("Hello ThreadPoolTaskExecutor");
            }
        };

        for (int i = 0; i < 10; i++) {
            taskExecutor.submit(task);
        }

        sleep(10000);
    }

执行的时候会报错
在这里插入图片描述

在ThreadPoolConfig中

@Configuration
@EnableScheduling
//如果不加EnableScheduling,则表明定时任务没有开启

@EnableAsync//使AlphaService中的@Async注解生效
public class ThreadPoolConfig {
}

执行结果:
在这里插入图片描述

Spring普通线程池 比jdk线程池更灵活,因为可以设置 核心线程个数,扩展线程个数

// Spring可执行定时任务的线程池
@Autowired
private ThreadPoolTaskScheduler taskScheduler;

    // 4.Spring定时任务线程池
    @Test
    public void testThreadPoolTaskScheduler() {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                logger.debug("Hello ThreadPoolTaskScheduler");
            }
        };

        Date startTime = new Date(System.currentTimeMillis() + 10000);//当前时间在延迟一万毫秒,就是现在的时间
        taskScheduler.scheduleAtFixedRate(task, startTime, 1000);//以固定频率执行,执行时间间隔

        sleep(30000);
    }

// 5.Spring普通线程池(简化)
@Test
public void testThreadPoolTaskExecutorSimple() {
    for (int i = 0; i < 10; i++) {
        alphaService.execute1();
    }

    sleep(10000);//不然线程还没执行,方法已经结束
}

在AlphaService中,

// 让该方法在多线程环境下,被异步的调用.即启动一个线程来执行此方法,该方法和主线程是并发(异步)执行的
@Async//之前ThreadPoolConfig中 @EnableAsync就是让此配置生效。
public void execute1() {
    logger.debug("execute1");
}

线程为啥要sleep呢

// 6.Spring定时任务线程池(简化)
    @Test
    public void testThreadPoolTaskSchedulerSimple() {
        sleep(30000);
    }

在这里插入图片描述
接下来演示Quartz,但是Quartz是依赖于数据库的。
在这里插入图片描述
DB有一套表,需要我们提前创建。这个表就是Quartz 所需要的表
该表即为Quartz依赖的表在这里插入图片描述
我们需要用命令行工具,用source命令,把其导入community这个库中。
导入后:
在这里插入图片描述
47.12

在maven里导入Quartz的包
在这里插入图片描述
然后看看源码:
job、Scheduler、jobdetail(用来配置job)、trigger触发器(设置以什么样的频率反复运行)

通过job接口定义一个任务,通过jobdetail以及trigger接口来配置job,主要做这三个事情。

配置好,重新启动,Quartz就会重新启动配置信息,并将读到的配置信息存到数据库(表)中,Quartz以后都会读取表中这些信息,来执行任务。
trigger第一次启动服务时用,以后就不用了
通过jobdetail配置的信息,都存到这个表里。
在这里插入图片描述
任务的名字
job的名字
job的分组
job的描述
job对应的类

新建AlphaJob 实现Job 接口

public class AlphaJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println(Thread.currentThread().getName() + ": execute a quartz job.");//打印当前线程的名字+: execute a quartz job.
    }
}

QuartzConfig

// 配置(仅在第一次被读取到) -> 信息被初始化到数据库 -> quartz访问数据库去调用该任务,不在访问配置文件
@Configuration
public class QuartzConfig {
    //FactoryBean和之前一开始学的IOC 学到的 BeanFactory有本质区别;BeanFactory是整个IOC容器的顶层接口
    // FactoryBean主要目的是 简化Bean的实例化过程,因为有的Bean实例化过程比较复杂:
    // 1.通过FactoryBean封装Bean的实例化过程.
    // 2.将FactoryBean装配到Spring容器里.
    // 3.将FactoryBean注入给其他的Bean.
    // 4.该Bean得到的是FactoryBean所管理的对象实例.


    // 配置JobDetail
    // @Bean
    public JobDetailFactoryBean alphaJobDetail() {//Bean的名字是alphaJobDetail。初始化该bean,想当于将其装配到容器中
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();//实例化对象
        factoryBean.setJobClass(AlphaJob.class);
        factoryBean.setName("alphaJob");//声明job任务的名字
        factoryBean.setGroup("alphaJobGroup");//声明任务的组
        factoryBean.setDurability(true);//声明任务是否长久保存,哪怕任务不再运行。连触发器都没有了,也会一直报存
        factoryBean.setRequestsRecovery(true);//声明任务是否可恢复
        return factoryBean;
    }

    // 配置Trigger(SimpleTriggerFactoryBean比较简单,每十天要做...; CronTriggerFactoryBean复杂,每个月月底前两天要做...)
    // @Bean
    public SimpleTriggerFactoryBean alphaTrigger(JobDetail alphaJobDetail) {//Trigger依赖于JobDetail,因此需要读取
        SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
        factoryBean.setJobDetail(alphaJobDetail);
        factoryBean.setName("alphaTrigger");
        factoryBean.setGroup("alphaTriggerGroup");
        factoryBean.setRepeatInterval(3000);//多长时间执行一次任务
        factoryBean.setJobDataMap(new JobDataMap());//Trigger底层需要存储一些状态,新建JobDataMap对象来存储
        return factoryBean;
    }

测试:在这里插入图片描述

Quartz底层也依赖于线程池,线程池有一默认配置,如果想重新配置底层线程池,需要在application properties中进行配置。


```c
# QuartzProperties
spring.quartz.job-store-type=jdbc//任务用jdbc来存储
spring.quartz.scheduler-name=communityScheduler//调度器的名字
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO//调度器的id 自动生成
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate//驱动StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.isClustered=true//是否采用集群方式,是
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool//用org.quartz.simpl.SimpleThreadPool线程池
spring.quartz.properties.org.quartz.threadPool.threadCount=5//线程数量

做了如上配置以后,
表里出现了如下信息:
在这里插入图片描述
在这里插入图片描述
新建QuartzTests

package com.nowcoder.community;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class QuartzTests {

    @Autowired
    private Scheduler scheduler;

    @Test
    public void testDeleteJob() {
        try {
            boolean result = scheduler.deleteJob(new JobKey("alphaJob", "alphaJobGroup"));
            System.out.println(result);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

}

返回true
确实删除了
在这里插入图片描述
这里面存着不是job,而是scheduler,所以东西还在
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值