關注微信公眾號:CodingTechWork,一起學習進步。
問題
在使用Spring
中的@Scheduled
注解設置定時任務時,遇到這樣2個問題:
- 定時任務未按時執行,現象是延后了一段時間才執行定時任務。
- 多個定時任務有時間重疊時,無法並發調度執行。
分析
出現上面問題現象的根因是Spring的定時任務默認是單線程執行,所以會在某些場景下造成阻塞。當然我們可以通過@Async
注解來異步執行這些並發的@Scheduled
注解的定時任務,而@Async
線程池容量是100,當超過100個線程並發執行時,則剩下的定時任務會等待之前的線程釋放,不會自行擴容。
既然@Async是個定值大小的線程池,還是有出現定時任務延時執行的問題,所以下面我們可以通過其他方式來自定義線程池大小。
解決方式
通過自定義配置線程池來解決問題。
package com.andya.selfcode.conf;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.*;
/**
* @author Andya
* @create 2021-03-31
*/
@Slf4j
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
//自定義線程池大小,可配置。代碼中默認10個
@Value("${threadPool.schedule.coreSize: 10}")
public int SCHEDULE_CORE_SIZE;
//自定義線程池名稱
public static final String THREAD_NAME_WITH_SCHEDULE = "schedule-thread-%d";
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
// scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(SCHEDULE_CORE_SIZE));
scheduledTaskRegistrar.setScheduler(this.buildSchedulerThreadPool());
System.out.println("Scheduler threadpool core_size: " + SCHEDULE_CORE_SIZE);
}
/**
* Spring的@Scheduled的自定義周期性線程池
* @return
*/
@Bean(value = "scheduleThreadPool")
public ExecutorService buildSchedulerThreadPool() {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(THREAD_NAME_WITH_SCHEDULE).build();
/**
* 1. CallerRunsPolicy : 這個策略重試添加當前的任務,他會自動重復調用 execute() 方法,直到成功。
2. AbortPolicy : 對拒絕任務拋棄處理,並且拋出異常。
3. DiscardPolicy : 對拒絕任務直接無聲拋棄,沒有異常信息。
4. DiscardOldestPolicy : 對拒絕任務不拋棄,而是拋棄隊列里面等待最久的一個線程,然后把拒絕任務加到隊列。
不寫則為默認的AbortPolicy策略。
*/
ScheduledExecutorService threadPool = new ScheduledThreadPoolExecutor(
SCHEDULE_CORE_SIZE,
threadFactory);
return threadPool;
}
}