玩轉SpringBoot之定時任務@Scheduled線程池配置


序言

對於定時任務,在SpringBoot中只需要使用@Scheduled 這個注解就能夠滿足需求,它的出現也給我們帶了很大的方便,我們只要加上該注解,並且根據需求設置好就可以使用定時任務了。

但是,我們需要注意的是,@Scheduled 並不一定會按時執行

因為使用@Scheduled 的定時任務雖然是異步執行的,但是,不同的定時任務之間並不是並行的!!!!!!!!

在其中一個定時任務沒有執行完之前,其他的定時任務即使是到了執行時間,也是不會執行的,它們會進行排隊。

也就是如果你想你不同的定時任務互不影響,到時間就會執行,那么你最好將你的定時任務方法自己搞成異步方法,這樣,定時任務其實就相當於調用了一個線程執行任務,一瞬間就結束了。比如使用:@Async

當然,也可以勉強將你的定時任務當做都會定時執行。但是,作為一個合格的程序員

那么,如何將@Scheduled實現的定時任務變成異步的呢?此時你需要對@Scheduled進行線程池配置。

配置示例

package com.java.navtool.business.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author :mmzsblog.cn
 * @date :Created in 2021/7/27 17:46
 * @description:spring-boot 多線程  @Scheduled注解 並發定時任務的解決方案
 * @modified By:
 * @version:
 */

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    public static final String EXECUTOR_SERVICE = "scheduledExecutor";

    @Bean(EXECUTOR_SERVICE)
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 設置核心線程數
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        // 設置最大線程數
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 10);
        // 設置隊列容量
        executor.setQueueCapacity(Runtime.getRuntime().availableProcessors() * 10);
        // 設置線程活躍時間(秒)
        executor.setKeepAliveSeconds(10);
        // 設置默認線程名稱
        executor.setThreadNamePrefix("scheduled-");
        // 設置拒絕策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任務結束后再關閉線程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }

}

附帶介紹一下線程池的幾個參數。需要徹底搞懂,不要死記硬背哦!

線程池參數

  • 1、corePoolSize(必填):核心線程數。
  • 2、maximumPoolSize(必填):最大線程數。
  • 3、keepAliveTime(必填):線程空閑時長。如果超過該時長,非核心線程就會被回收。
  • 4、unit(必填):指定keepAliveTime的時間單位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
  • 5、workQueue(必填):任務隊列。通過線程池的execute()方法提交的Runnable對象將存儲在該隊列中。
  • 6、threadFactory(可選):線程工廠。一般就用默認的。
  • 7、handler(可選):拒絕策略。當線程數達到最大線程數時就要執行飽和策略。

說下核心線程數和最大線程數的區別:

拒絕策略可選值:

  • 1、AbortPolicy(默認):放棄任務並拋出RejectedExecutionException異常。
  • 2、CallerRunsPolicy:由調用線程處理該任務。
  • 3、DiscardPolicy:放棄任務,但是不拋出異常。可以配合這種模式進行自定義的處理方式。
  • 4、DiscardOldestPolicy:放棄隊列最早的未處理任務,然后重新嘗試執行任務。

線程池執行流程:

上個流程圖,先試着自己看下能不能看懂:

簡短的總結下線程池執行流程:

  • 1、一個任務提交到線程池后,如果當前的線程數沒達到核心線程數,則新建一個線程並且執行新任務,注意一點,這個新任務執行完后,該線程不會被銷毀;
  • 2、如果達到了,則判斷任務隊列滿了沒,如果沒滿,則將任務放入任務隊列;
  • 3、如果滿了,則判斷當前線程數量是否達到最大線程數,如果沒達到,則創建新線程來執行任務,注意,如果線程池中線程數量大於核心線程數,每當有線程超過了空閑時間,就會被銷毀,直到線程數量不大於核心線程數;
  • 4、如果達到了最大線程數,並且任務隊列滿了,就會執行飽和策略;


免責聲明!

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



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