美文网首页javaIT必备技能
SpringBoot—自定义线程池及并发定时任务模板

SpringBoot—自定义线程池及并发定时任务模板

作者: Hughman | 来源:发表于2020-09-10 08:46 被阅读0次

    关注:CodingTechWork,一起学习进步。

    介绍

      在项目开发中,经常遇到定时任务,今天通过自定义多线程池总结一下SpringBoot默认实现的定时任务机制。

    定时任务模板

    pom依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        //线程池用到
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>22.0</version>
        </dependency>
        //@Slf4j注解用到
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.4</version>
        </dependency>
    </dependencies>
    

    自定义线程池模板

    package com.example.andya.demo.conf;
    
    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.concurrent.*;
    
    /**
     * @author Andya
     * @create 2020-05-29 14:08
     */
    @Configuration
    public class ThreadPoolConfig {
    
        public static String THREAD_NAME = "first-thread-pool-%d";
        public static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2 + 1;
        public static int MAX_POOL_SIZE = 10;
        public static int QUEUE_SIZE = 10;
    
        /**
         * 自定义消费队列线程池
         *
         * @return
         */
        @Bean(value = "firstThreadPool")
        public ExecutorService buildFirstThreadPool() {
            ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(THREAD_NAME).build();
    
            /**
             * 1. CallerRunsPolicy :    这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
             2. AbortPolicy :         对拒绝任务抛弃处理,并且抛出异常。
             3. DiscardPolicy :       对拒绝任务直接无声抛弃,没有异常信息。
             4. DiscardOldestPolicy : 对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个线程,然后把拒绝任务加到队列。
             */
            ExecutorService threadPool = new ThreadPoolExecutor(
                    CORE_POOL_SIZE,
                    MAX_POOL_SIZE,
                    0L,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(QUEUE_SIZE),
                    threadFactory,
                    new ThreadPoolExecutor.AbortPolicy());
            return threadPool;
        }
    }
    
    

    定时任务模板

    package com.example.andya.demo.service;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author Andya
     * @create 2020-05-29 14:26
     */
    @Service
    @EnableScheduling
    @Slf4j
    public class TestThreadPool {
        @Resource(name = "firstThreadPool")
        private ExecutorService firstThreadPool;
    
        @Scheduled(cron = "0 * * * * *")
        public void test1SchedulerThreadPool() {
            final CountDownLatch countDownLatch = new CountDownLatch(5);
            log.info("Begin schedule-【1】 startTime: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            for (int i = 0; i < 5; i++) {
                firstThreadPool.execute(() -> {
                    countDownLatch.countDown();
                    log.info("schedule-【1】 , threadPool info: {}, countDownLatch info: {}", firstThreadPool.toString(), countDownLatch.toString());
                });
            }
            try {
                countDownLatch.await(5, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                log.error("schedule-【1】timeout, {}", e.getMessage());
            } finally {
                log.info("schedule-【1】 multi-threading countDownLatch count: {}",  countDownLatch.getCount());
            }
            log.info("End schedule-【1】 startTime: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        }
    
        @Scheduled(cron = "0 * * * * *")
        public void test2SchedulerThreadPool() {
            final CountDownLatch countDownLatch = new CountDownLatch(5);
            log.info("Begin schedule-【2】 startTime: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            for (int i = 0; i < 5; i++) {
                firstThreadPool.execute(() -> {
                    countDownLatch.countDown();
                    log.info("schedule-【2】, threadPool info {}, countDownLatch info: {}", firstThreadPool.toString(), countDownLatch.toString());
                });
            }
            try {
                countDownLatch.await(5, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                log.error("schedule-【2】timeout, {}", e.getMessage());
            } finally {
                log.info("schedule-【2】 multi-threading countDownLatch count: {}",  countDownLatch.getCount());
            }
            log.info("End schedule-【2】 startTime: {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        }
    }
    
    

    运行结果

    2020-05-29 15:45:00.011  INFO 6900 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : Begin schedule-【2】 startTime: 2020-05-29 15:45:00
    2020-05-29 15:45:00.018  INFO 6900 --- [t-thread-pool-0] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 5, active threads = 5, queued tasks = 0, completed tasks = 0], countDownLatch info: java.util.concurrent.CountDownLatch@522af59c[Count = 4]
    2020-05-29 15:45:00.019  INFO 6900 --- [t-thread-pool-1] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 5, active threads = 4, queued tasks = 0, completed tasks = 1], countDownLatch info: java.util.concurrent.CountDownLatch@522af59c[Count = 3]
    2020-05-29 15:45:00.019  INFO 6900 --- [t-thread-pool-2] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 5, active threads = 3, queued tasks = 0, completed tasks = 2], countDownLatch info: java.util.concurrent.CountDownLatch@522af59c[Count = 2]
    2020-05-29 15:45:00.019  INFO 6900 --- [t-thread-pool-3] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 5, active threads = 2, queued tasks = 0, completed tasks = 3], countDownLatch info: java.util.concurrent.CountDownLatch@522af59c[Count = 1]
    2020-05-29 15:45:00.019  INFO 6900 --- [t-thread-pool-4] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 5, active threads = 1, queued tasks = 0, completed tasks = 4], countDownLatch info: java.util.concurrent.CountDownLatch@522af59c[Count = 0]
    2020-05-29 15:45:00.019  INFO 6900 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : schedule-【2】 multi-threading countDownLatch count: 0
    2020-05-29 15:45:00.019  INFO 6900 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : End schedule-【2】 startTime: 2020-05-29 15:45:00
    2020-05-29 15:45:00.020  INFO 6900 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : Begin schedule-【1】 startTime: 2020-05-29 15:45:00
    2020-05-29 15:45:00.021  INFO 6900 --- [t-thread-pool-0] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 9, active threads = 5, queued tasks = 0, completed tasks = 5], countDownLatch info: java.util.concurrent.CountDownLatch@5ced01a9[Count = 4]
    2020-05-29 15:45:00.023  INFO 6900 --- [t-thread-pool-5] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 9, active threads = 4, queued tasks = 0, completed tasks = 6], countDownLatch info: java.util.concurrent.CountDownLatch@5ced01a9[Count = 3]
    2020-05-29 15:45:00.024  INFO 6900 --- [t-thread-pool-6] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 9, active threads = 3, queued tasks = 0, completed tasks = 7], countDownLatch info: java.util.concurrent.CountDownLatch@5ced01a9[Count = 2]
    2020-05-29 15:45:00.024  INFO 6900 --- [t-thread-pool-7] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 9, active threads = 3, queued tasks = 0, completed tasks = 7], countDownLatch info: java.util.concurrent.CountDownLatch@5ced01a9[Count = 1]
    2020-05-29 15:45:00.025  INFO 6900 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 multi-threading countDownLatch count: 0
    2020-05-29 15:45:00.025  INFO 6900 --- [t-thread-pool-8] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@26b894bd[Running, pool size = 9, active threads = 3, queued tasks = 0, completed tasks = 7], countDownLatch info: java.util.concurrent.CountDownLatch@5ced01a9[Count = 0]
    2020-05-29 15:45:00.025  INFO 6900 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : End schedule-【1】 startTime: 2020-05-29 15:45:00
    

    从上述结果中可以看出,虽然是test1SchedulerThreadPool()test2SchedulerThreadPool()都是每分钟执行定时任务,但是明显两个方法没有并发执行,而是串行执行的。

    并发定时器模板

    通过增加一个配置类来并发执行定时任务。

    package com.example.andya.demo.conf;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.SchedulingConfigurer;
    import org.springframework.scheduling.config.ScheduledTaskRegistrar;
    
    import java.util.concurrent.Executors;
    
    /**
     * @author Andya
     * @create 2020-05-29 15:52
     */
    @Configuration
    public class ScheduleConfig implements SchedulingConfigurer {
        @Override
        public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
            scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
        }
    }
    
    

    运行结果

    2020-05-29 15:53:00.005  INFO 3028 --- [pool-2-thread-2] c.e.andya.demo.service.TestThreadPool    : Begin schedule-【2】 startTime: 2020-05-29 15:53:00
    2020-05-29 15:53:00.015  INFO 3028 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : Begin schedule-【1】 startTime: 2020-05-29 15:53:00
    2020-05-29 15:53:00.019  INFO 3028 --- [t-thread-pool-0] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 9, queued tasks = 1, completed tasks = 0], countDownLatch info: java.util.concurrent.CountDownLatch@630f649f[Count = 3]
    2020-05-29 15:53:00.020  INFO 3028 --- [t-thread-pool-3] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 9, queued tasks = 1, completed tasks = 0], countDownLatch info: java.util.concurrent.CountDownLatch@630f649f[Count = 1]
    2020-05-29 15:53:00.019  INFO 3028 --- [t-thread-pool-1] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 9, queued tasks = 1, completed tasks = 0], countDownLatch info: java.util.concurrent.CountDownLatch@630f649f[Count = 3]
    2020-05-29 15:53:00.019  INFO 3028 --- [t-thread-pool-2] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 9, queued tasks = 1, completed tasks = 0], countDownLatch info: java.util.concurrent.CountDownLatch@630f649f[Count = 2]
    2020-05-29 15:53:00.021  INFO 3028 --- [t-thread-pool-0] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 6, queued tasks = 0, completed tasks = 4], countDownLatch info: java.util.concurrent.CountDownLatch@5ebb120e[Count = 4]
    2020-05-29 15:53:00.021  INFO 3028 --- [t-thread-pool-4] c.e.andya.demo.service.TestThreadPool    : schedule-【2】, threadPool info java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 5, queued tasks = 0, completed tasks = 5], countDownLatch info: java.util.concurrent.CountDownLatch@630f649f[Count = 0]
    2020-05-29 15:53:00.021  INFO 3028 --- [pool-2-thread-2] c.e.andya.demo.service.TestThreadPool    : schedule-【2】 multi-threading countDownLatch count: 0
    2020-05-29 15:53:00.022  INFO 3028 --- [pool-2-thread-2] c.e.andya.demo.service.TestThreadPool    : End schedule-【2】 startTime: 2020-05-29 15:53:00
    2020-05-29 15:53:00.022  INFO 3028 --- [t-thread-pool-5] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 4, queued tasks = 0, completed tasks = 6], countDownLatch info: java.util.concurrent.CountDownLatch@5ebb120e[Count = 3]
    2020-05-29 15:53:00.022  INFO 3028 --- [t-thread-pool-6] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 3, queued tasks = 0, completed tasks = 7], countDownLatch info: java.util.concurrent.CountDownLatch@5ebb120e[Count = 2]
    2020-05-29 15:53:00.024  INFO 3028 --- [t-thread-pool-7] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 2, queued tasks = 0, completed tasks = 8], countDownLatch info: java.util.concurrent.CountDownLatch@5ebb120e[Count = 1]
    2020-05-29 15:53:00.024  INFO 3028 --- [t-thread-pool-8] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 , threadPool info: java.util.concurrent.ThreadPoolExecutor@5149f008[Running, pool size = 9, active threads = 2, queued tasks = 0, completed tasks = 8], countDownLatch info: java.util.concurrent.CountDownLatch@5ebb120e[Count = 0]
    2020-05-29 15:53:00.024  INFO 3028 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : schedule-【1】 multi-threading countDownLatch count: 0
    2020-05-29 15:53:00.024  INFO 3028 --- [pool-2-thread-1] c.e.andya.demo.service.TestThreadPool    : End schedule-【1】 startTime: 2020-05-29 15:53:00
    

    可以从运行结果中看到,test1SchedulerThreadPool()test2SchedulerThreadPool()两个方法同时打印了Begin schedule...信息,是并发定时。

    @Async实现定时并发

    除了上述这种实现SchedulingConfigurer类来实现定时任务的并发,还可以通过@EnableAsync@Async注解实现定时任务的并发。

    package com.example.andya.demo.util;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    
    import java.time.LocalDateTime;
    
    /**
     * @author Andya
     * @create 2020-04-20 22:50
     */
    @Configuration
    @EnableScheduling
    @EnableAsync
    public class StaticScheduleTask {
    
        private Logger LOG = LoggerFactory.getLogger(StaticScheduleTask.class);
    
        @Async
        @Scheduled(cron = "${schedule.cron}")
        public void firstTask() throws InterruptedException {
            for (int i = 0; i < 5; i++) {
                LOG.info("first task time: " + LocalDateTime.now() + "\r\nthread:" + Thread.currentThread().getName());
            }
            Thread.sleep(1000 * 10);
        }
    
        @Async
    //    @Scheduled(fixedDelay = 5000)
        @Scheduled(cron = "0 * * * * *")
        public void secondTask() throws InterruptedException {
            for (int i = 0; i < 5; i++) {
                LOG.info("second task time: " + LocalDateTime.now() + "\r\nthread:" + Thread.currentThread().getName());
            }
            Thread.sleep(1000 * 10);
        }
    }
    
    
    

    运行结果:

    
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-1] c.e.andya.demo.util.StaticScheduleTask   : second task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-1
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-2] c.e.andya.demo.util.StaticScheduleTask   : first task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-2
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-1] c.e.andya.demo.util.StaticScheduleTask   : second task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-1
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-2] c.e.andya.demo.util.StaticScheduleTask   : first task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-2
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-1] c.e.andya.demo.util.StaticScheduleTask   : second task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-1
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-2] c.e.andya.demo.util.StaticScheduleTask   : first task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-2
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-1] c.e.andya.demo.util.StaticScheduleTask   : second task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-1
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-2] c.e.andya.demo.util.StaticScheduleTask   : first task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-2
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-1] c.e.andya.demo.util.StaticScheduleTask   : second task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-1
    2020-05-29 16:07:00.035  INFO 31428 --- [cTaskExecutor-2] c.e.andya.demo.util.StaticScheduleTask   : first task time: 2020-05-29T16:07:00.035
    thread:SimpleAsyncTaskExecutor-2
    

    相关文章

      网友评论

        本文标题:SpringBoot—自定义线程池及并发定时任务模板

        本文链接:https://www.haomeiwen.com/subject/xtteektx.html