SpringBoot—自定義線程池及並發定時任務模板


介紹

  在項目開發中,經常遇到定時任務,今天通過自定義多線程池總結一下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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM