最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot @Schedule的使用注意與原理分析

 更新時間:2024年08月02日 11:22:21   作者:trayvontang  
這篇文章主要介紹了SpringBoot @Schedule的使用注意與原理分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

簡介

之前使用@Schedule一直沒有遇到什么問題,那種拿來就用的感覺還挺好,最近使用@Schedule遇到一點問題,才仔細(xì)的研究了一下@Schedule的一些細(xì)節(jié)和原理問題。

這篇文章就將分享一下,使用@Schedule一些可能被忽略的問題。

注意事項

@Schedule默認(rèn)線程池大小

我相信@Schedule默認(rèn)線程池大小的問題肯定是被很多拿來就用的朋友忽略的問題,默認(rèn)情況下@Schedule使用線程池的大小為1。

一般情況下沒有什么問題,但是如果有多個定時任務(wù),每個定時任務(wù)執(zhí)行時間可能不短的情況下,那么有的定時任務(wù)可能一直沒有機會執(zhí)行。

有興趣的朋友,可以試一下:

@Component
public class BrigeTask {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Scheduled(cron = "*/5 * * * * ?")
    private void cron() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "-cron:" + LocalDateTime.now().format(FORMATTER));
        TimeUnit.SECONDS.sleep(6);
    }

    @Scheduled(fixedDelay = 5000)
    private void fixedDelay() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "-fixedDelay:" + LocalDateTime.now().format(FORMATTER));
        TimeUnit.SECONDS.sleep(6);
    }

    @Scheduled(fixedRate = 5000)
    private void fixedRate() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "-fixedRate:" + LocalDateTime.now().format(FORMATTER));
        TimeUnit.SECONDS.sleep(6);
    }
}

上面的任務(wù)中,fixedDelay與cron,可能很久都不會被執(zhí)行。

要解決上面的問題,可以把執(zhí)行任務(wù)的線程池設(shè)置大一點,怎樣設(shè)置通過實現(xiàn)SchedulingConfigurer接口,在configureTasks方法中配置,這種方式參見后面的代碼,這里可以直接注入一個TaskScheduler來解決問題。

@Bean
public TaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
    taskScheduler.setPoolSize(5);
    return taskScheduler;
}

當(dāng)然也可以使用ScheduledExecutorService:

@Bean
public ScheduledExecutorService scheduledExecutorService() {
    return Executors.newScheduledThreadPool(10);
}

為啥這樣有效,請參考后面@Schedule原理。

固定延遲與固定速率

@Schedule的三種方式cron、fixedDelay、fixedRate不管線程夠不夠都會阻塞到上一次執(zhí)行完成,才會執(zhí)行下一次。

如果任務(wù)方法執(zhí)行時間非常短,上面三種方式其實基本沒有太多的區(qū)別。

如果,任務(wù)方法執(zhí)行時間比較長,大于了設(shè)置的執(zhí)行周期,那么就有很大的區(qū)別。例如,假設(shè)執(zhí)行任務(wù)的線程足夠,執(zhí)行周期是5s,任務(wù)方法會執(zhí)行6s。

  • cron的執(zhí)行方式是,任務(wù)方法執(zhí)行完,遇到下一次匹配的時間再次執(zhí)行,基本就會10s執(zhí)行一次,因為執(zhí)行任務(wù)方法的時間區(qū)間會錯過一次匹配。
  • fixedDelay的執(zhí)行方式是,方法執(zhí)行了6s,然后會再等5s再執(zhí)行下一次,在上面的條件下,基本就是每11s執(zhí)行一次。
  • fixedRate的執(zhí)行方式就變成了每隔6s執(zhí)行一次,因為按固定區(qū)間執(zhí)行它沒5s就應(yīng)該執(zhí)行一次,但是任務(wù)方法執(zhí)行了6s,沒辦法,只好6s執(zhí)行一次。

上面的結(jié)論都可以通過,最上面的示例驗證,有興趣的朋友可以調(diào)整一下休眠時間測試一下。

SpringBoot @Schedule原理

在SpringBoot中,我們使用@EnableScheduling來啟用@Schedule。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}

EnableScheduling注解沒什么特殊,需要注意import了SchedulingConfiguration。

SchedulingConfiguration一看名字就知道是一個配置類,肯定是為了添加相應(yīng)的依賴類。

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
		return new ScheduledAnnotationBeanPostProcessor();
	}
}

我們可以看到在SchedulingConfiguration創(chuàng)建了一個ScheduledAnnotationBeanPostProcessor。

看樣子SpringBoot定時任務(wù)的核心就是ScheduledAnnotationBeanPostProcessor類了,下面我們來看一看ScheduledAnnotationBeanPostProcessor類。

ScheduledAnnotationBeanPostProcessor

public class ScheduledAnnotationBeanPostProcessor
		implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor,
		Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware,
		SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
}

ScheduledAnnotationBeanPostProcessor實現(xiàn)了很多接口,這里重點關(guān)注2個,ApplicationListener和DestructionAwareBeanPostProcessor。

DestructionAwareBeanPostProcessor封裝任務(wù)

DestructionAwareBeanPostProcessor繼承了BeanPostProcessor。

BeanPostProcessor相信大家已經(jīng)非常熟悉了,就是在Bean創(chuàng)建執(zhí)行setter之后,在自定義的afterPropertiesSet和init-method前后提供攔截點,大致執(zhí)行的先后順序是:

Bean實例化 -> setter -> BeanPostProcessor#postProcessBeforeInitialization ->
-> InitializingBean#afterPropertiesSet -> init-method -> BeanPostProcessor#postProcessAfterInitialization

我們看一下ScheduledAnnotationBeanPostProcessor的postProcessAfterInitialization方法:

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler ||
            bean instanceof ScheduledExecutorService) {
        // Ignore AOP infrastructure such as scoped proxies.
        return bean;
    }

    Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
    if (!this.nonAnnotatedClasses.contains(targetClass) &&
            AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
        Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
                (MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
                    Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                            method, Scheduled.class, Schedules.class);
                    return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
                });
        if (annotatedMethods.isEmpty()) {
            this.nonAnnotatedClasses.add(targetClass);
            if (logger.isTraceEnabled()) {
                logger.trace("No @Scheduled annotations found on bean class: " + targetClass);
            }
        }
        else {
            // Non-empty set of methods
            annotatedMethods.forEach((method, scheduledMethods) ->
                    scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
            if (logger.isTraceEnabled()) {
                logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
                        "': " + annotatedMethods);
            }
        }
    }
    return bean;
}

簡單說一下流程:

找到所有的Schedule方法,把它封裝為ScheduledMethodRunnable類(ScheduledMethodRunnable類實現(xiàn)了Runnable接口),并把其做為一個任務(wù)注冊到ScheduledTaskRegistrar中。

如果對具體的邏輯感興趣,可以從postProcessAfterInitialization方法順著processScheduled方法一次debug。

ApplicationListener執(zhí)行任務(wù)

前面我們介紹通過BeanPostProcessor解析出了所有的任務(wù),接下來要做的事情就是提交任務(wù)了。

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    if (event.getApplicationContext() == this.applicationContext) {
        // Running in an ApplicationContext -> register tasks this late...
        // giving other ContextRefreshedEvent listeners a chance to perform
        // their work at the same time (e.g. Spring Batch's job registration).
        finishRegistration();
    }
}

ScheduledAnnotationBeanPostProcessor監(jiān)聽的事件是ContextRefreshedEvent,就是在容器初始化,或者刷新的時候被調(diào)用。

監(jiān)聽到ContextRefreshedEvent事件之后,值調(diào)用了finishRegistration方法,這個方法的基本流程如下:

  • 1.找到容器中的SchedulingConfigurer,并調(diào)用它的configureTasks,SchedulingConfigurer的作用主要就是配置ScheduledTaskRegistrar類,例如線程池等參數(shù),例如:
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executors;

@Configuration
public class MyScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }
}
  • 2.調(diào)用ScheduledTaskRegistrar的afterPropertiesSet方法執(zhí)行任務(wù),如果對具體的邏輯感興趣,可以閱讀ScheduledTaskRegistrar的scheduleTasks方法。

關(guān)于為啥直接在容器中注入一個TaskScheduler、ScheduledExecutorService也可以有效,也可以在finishRegistration方法中找到答案。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot?使用log4j2的配置過程

    SpringBoot?使用log4j2的配置過程

    這篇文章主要介紹了SpringBoot?使用log4j2的配置,springboot默認(rèn)是用logback的日志框架的,所以要在pom中配置排除logback。這里需要注意的是,其實不止一處使用了logback,所以要在starter中統(tǒng)一排除,然后引入log4j2,需要的朋友可以參考下
    2022-09-09
  • 詳解SpringBoot整合MyBatis詳細(xì)教程

    詳解SpringBoot整合MyBatis詳細(xì)教程

    這篇文章主要介紹了詳解SpringBoot整合MyBatis詳細(xì)教程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • spring初始化源碼代碼淺析

    spring初始化源碼代碼淺析

    Spring框架被廣泛應(yīng)用于我們的日常工作中,但是很長時間以來我們都是只會使用,不懂它的作用原理,下面這篇文章主要給大家介紹了關(guān)于spring初始化源碼的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • 簡單談?wù)刯ava中final,finally,finalize的區(qū)別

    簡單談?wù)刯ava中final,finally,finalize的區(qū)別

    Java中final、finally、finalize的區(qū)別與用法,困擾了不少學(xué)習(xí)者,下面我們就這個問題進(jìn)行一些探討,希望對大家的學(xué)習(xí)有所幫助。
    2016-05-05
  • Java中synchronized關(guān)鍵字鎖的力度與位置示例詳解

    Java中synchronized關(guān)鍵字鎖的力度與位置示例詳解

    synchronized是Java中最核心的線程同步機制,用于解決多線程環(huán)境下的數(shù)據(jù)競爭和可見性問題,這篇文章主要介紹了Java中synchronized關(guān)鍵字鎖的力度與位置的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2026-01-01
  • 關(guān)于fastjson的常見API詳解

    關(guān)于fastjson的常見API詳解

    這篇文章主要介紹了關(guān)于fastjson的常見API詳解,Fastjson是一個Java庫,可用于將Java對象轉(zhuǎn)換為其JSON表示,它還可用于將JSON字符串轉(zhuǎn)換為等效的Java對象,Fastjson可以處理任意Java對象,包括您沒有源代碼的預(yù)先存在的對象,需要的朋友可以參考下
    2023-07-07
  • java利用easyexcel實現(xiàn)導(dǎo)入與導(dǎo)出功能

    java利用easyexcel實現(xiàn)導(dǎo)入與導(dǎo)出功能

    這篇文章主要介紹了java利用easyexcel實現(xiàn)導(dǎo)入與導(dǎo)出功能,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助
    2022-09-09
  • SpringCloud?OpenFeign概述與使用教程

    SpringCloud?OpenFeign概述與使用教程

    OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成
    2023-02-02
  • SpringBoot使用AOP統(tǒng)一日志管理的方法詳解

    SpringBoot使用AOP統(tǒng)一日志管理的方法詳解

    這篇文章主要為大家分享一個干貨:超簡潔SpringBoot使用AOP統(tǒng)一日志管理,文中的示例代碼講解詳細(xì),感興趣的小伙伴快跟隨小編一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • java去除圖片水印的四種方法實現(xiàn)與對比

    java去除圖片水印的四種方法實現(xiàn)與對比

    這篇文章將從簡單到復(fù)雜,為大家詳細(xì)介紹幾種常見的 Java 去水印方法、適用的場景以及需要注意的事項,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2025-08-08

最新評論

江山市| 昆明市| 华宁县| 庆元县| 崇义县| 马公市| 惠来县| 尼木县| 莲花县| 永城市| 台南县| 建阳市| 金昌市| 陵川县| 英德市| 周至县| 翁源县| 延安市| 三门县| 济源市| 米易县| 新和县| 台中县| 怀柔区| 苏尼特右旗| 江都市| 舟山市| 宁夏| 游戏| 涞水县| 英吉沙县| 黄浦区| 香河县| 马边| 南江县| 开江县| 长兴县| 西盟| 怀集县| 建阳市| 镇康县|