JDK线程池
(1)ExecutorService
(2)ScheduledExecutorService
  Spring线程池
(1)ThreadPoolTaskExecutor
(2)ThreadPoolTaskSchedular
  分布式定时任务
Spring Quartz(官网http://www.quartz-schedular.org)
  JDK的线程池在分布式环境下有问题(因为它数据是存在内存的)
直接用jdk的线程池在分布式环境下的问题
  而Quartz用数据库来存数据,适用于分布式环境
quartz适用于分布式环境
  新建ThreadPoolTests类,

private void sleep(long 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++){
            executorService.submit(task);
        }

        sleep(10000);  //10秒,以免任务未执行完就结束了
    }

    // 2.JDK定时任务线程池
    @Test
    public void testScheduledExecutorService(){
        Runnable task = new Runnable() {  //先实现Runnable接口,得到一个线程体
            @Override
            public void run() {
                logger.debug("Hello ScheduledExecutorService");
            }
        };

        scheduledExecutorService.scheduleAtFixedRate(task, 10000, 1000, TimeUnit.MILLISECONDS); //延迟10000毫秒再开始执行
        sleep(30000);
    }

  在application-properties里对Spring的线程池进行配置

# TaskExecutionProperties
spring.task.execution.pool.core-size=5
spring.task.execution.pool.max-size=15
spring.task.execution.pool.queue-capacity=100

# TaskSchedulingProperties
spring.task.scheduling.pool.size=5

  新建ThreadPoolConfig类,

@Configuration
@EnableScheduling
@EnableAsync
public class ThreadPoolConfig {
}

   在ThreadPoolTests类里,添加

//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); //阻塞一下
    }

    // 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); //当前时间再延迟10秒开始执行,这里不需要指定时间的单位,是因为spring这个方法已经内部默认以毫秒为单位
        taskScheduler.scheduleAtFixedRate(task,startTime,1000);

        sleep(30000); //阻塞30秒
    }

  在AlphaService类里,添加

	private static final Logger logger = LoggerFactory.getLogger(AlphaService.class);

	//让该方法在多线程环境下,被异步地调用
    @Async
    public void execute1(){  //和主线程并行执行
        logger.debug("execute1");
    }

  在ThreadPoolTests类里添加

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

  在AlphaService类里添加

  @Scheduled(initialDelay = 10000,fixedRate = 1000)
    public void execute2(){
        logger.debug("execute2");
    }

  在ThreadPoolTests类里添加

// 5.Spring定时线程池(简化)
    @Test
    public void testThreadPoolTaskSchedulerSimple(){
//        alphaService.execute2();
        sleep(30000);
    }

  接下来学习quartz。需提前创建表。需要导包,搜索spring boot quartz,吧spring-boot-starter-quartz拷贝到pom.xml。
  这个包里Scheduler是核心类(底层已经实例化好了),它里面有个Job接口(需要我们写),JobDetail是用来配置Job的。还有一个接口Trigger,规定这个Job什么时候运行,以什么样的频率运行。这些个配置只是在第一次启动的时候使用,以后就固定化到数据库里了。
  qrtz_scheduler_state表里存的是定时器的信息,qrtz_locks是定时器的锁,当有多个定时器的时候,按照这个表来加锁。
  新建quartz包,新建AlphaJob类

public class AlphaJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println(Thread.currentThread().getName()+":execute a quartz job.");
    }
}

  在config包下新建QuartzConfig类,

// FactoryBean可简化Bean的实例化过程(可以认为它底层封装了jobdetal详细的实例化过程)
    // 1.通过FactoryBean封装Bean的实例化过程
    // 2.将FactoryBean装配到Spring容器里
    // 3.将FactoryBean注入给其他的Bean
    // 4.该Bean得到的是FactoryBean所管理的对象实例


//配置JobDetail
    @Bean
    public JobDetailFactoryBean alphaJobDetail(){  //FactoryBean与spring IOC顶层的BeanFactory不同
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(AlphaJob.class);
        factoryBean.setName("alphaJob");
        factoryBean.setGroup("alphaJobGroup");
        factoryBean.setDurability(true); //哪怕将来这个任务不再运行了,甚至它的Trigger都没有了,也要留在数据库(不删)
        factoryBean.setRequestsRecovery(true);
        return factoryBean;
    }

    // 配置Trigger(可用SimpleTriggerFactoryBean或复杂的ConTriggerFactoryBean(比如半夜做事,它里面有表达式))
    @Bean
    public SimpleTriggerFactoryBean alphaTrigger(JobDetail alphaJobDetail){  //JobDetail参数alphaJobDetail要与上面定义alphaJobDetail()的名字一致,以保证有多个实例的时候,优先选择名字一致的
        SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
        factoryBean.setJobDetail(alphaJobDetail);
        factoryBean.setName("alphaTrigger");
        factoryBean.setGroup("alphaTriggerGroup");
        factoryBean.setRepeatInterval(3000); //每3秒
        factoryBean.setJobDataMap(new JobDataMap()); //用来存储job的状态
        return factoryBean;
    }

  在application-properties里添加

# QuartzProperties
spring.quartz.job-store-type=jdbc
spring.quartz.scheduler-name=communityScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
#存储到数据库的方式
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#存储到数据库的引擎驱动
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount=5

  如果不想要这个定时任务了,可以手动删除数据库里相关数据,或者写一个测试类QuartzTests

    @Autowired
    private Scheduler scheduler;

    @Test
    public void testDeleteJob(){
        try {
            boolean result = scheduler.deleteJob(new JobKey("alphaJob","alphaJobGroup")); //通过name和group可以唯一地确定一个job
            System.out.println(result);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

  观察到数据库job和trigger的表清空了相关信息。还要注意,再次启动前,QuartzConfig的@Bean注释掉。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐