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

SpringBoot中定時任務(wù)@Scheduled注解的使用解讀

 更新時間:2022年09月30日 14:15:22   作者:Sunshine_Dongyang  
這篇文章主要介紹了SpringBoot中定時任務(wù)@Scheduled注解的使用解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

項目開發(fā)中,經(jīng)常會遇到定時任務(wù)的場景,Spring提供了@Scheduled注解,方便進(jìn)行定時任務(wù)的開發(fā)

概述

要使用@Scheduled注解,首先需要在啟動類添加@EnableScheduling,啟用Spring的計劃任務(wù)執(zhí)行功能,這樣可以在容器中的任何Spring管理的bean上檢測@Scheduled注解,執(zhí)行計劃任務(wù)

注解定義

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {

	String cron() default "";

	String zone() default "";

	long fixedDelay() default -1;

	String fixedDelayString() default "";

	long fixedRate() default -1;

	String fixedRateString() default "";
	
	long initialDelay() default -1;

	String initialDelayString() default "";
}

參數(shù)說明

參數(shù)參數(shù)說明示例
cron任務(wù)執(zhí)行的cron表達(dá)式0/1 * * * * ?
zonecron表達(dá)時解析使用的時區(qū),默認(rèn)為服務(wù)器的本地時區(qū),使用java.util.TimeZone#getTimeZone(String)方法解析GMT-8:00
fixedDelay上一次任務(wù)執(zhí)行結(jié)束到下一次執(zhí)行開始的間隔時間,單位為ms1000
fixedDelayString上一次任務(wù)執(zhí)行結(jié)束到下一次執(zhí)行開始的間隔時間,使用java.time.Duration#parse解析PT15M
fixedRate以固定間隔執(zhí)行任務(wù),即上一次任務(wù)執(zhí)行開始到下一次執(zhí)行開始的間隔時間,單位為ms,若在調(diào)度任務(wù)執(zhí)行時,上一次任務(wù)還未執(zhí)行完畢,會加入worker隊列,等待上一次執(zhí)行完成后立即執(zhí)行下一次任務(wù)2000
fixedRateString與fixedRate邏輯一致,只是使用java.time.Duration#parse解析PT15M
initialDelay首次任務(wù)執(zhí)行的延遲時間1000
initialDelayString首次任務(wù)執(zhí)行的延遲時間,使用java.time.Duration#parse解析PT15M

源碼解析

配置了@Scheduled注解的方法,Spring的處理是通過注冊ScheduledAnnotationBeanPostProcessor來執(zhí)行,將不同配置參數(shù)的任務(wù)分配給不同的handler處理,核心代碼如下

org.springframework.scheduling.annotation.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;
}

org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#processScheduled

/**
 * Process the given {@code @Scheduled} method declaration on the given bean.
 * @param scheduled the @Scheduled annotation
 * @param method the method that the annotation has been declared on
 * @param bean the target bean instance
 * @see #createRunnable(Object, Method)
 */
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
	try {
		Runnable runnable = createRunnable(bean, method);
		boolean processedSchedule = false;
		String errorMessage =
				"Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
		Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
		// Determine initial delay
		long initialDelay = scheduled.initialDelay();
		String initialDelayString = scheduled.initialDelayString();
		if (StringUtils.hasText(initialDelayString)) {
			Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
			if (this.embeddedValueResolver != null) {
				initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
			}
			if (StringUtils.hasLength(initialDelayString)) {
				try {
					initialDelay = parseDelayAsLong(initialDelayString);
				}
				catch (RuntimeException ex) {
					throw new IllegalArgumentException(
							"Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into long");
				}
			}
		}
		// Check cron expression
		String cron = scheduled.cron();
		if (StringUtils.hasText(cron)) {
			String zone = scheduled.zone();
			if (this.embeddedValueResolver != null) {
				cron = this.embeddedValueResolver.resolveStringValue(cron);
				zone = this.embeddedValueResolver.resolveStringValue(zone);
			}
			if (StringUtils.hasLength(cron)) {
				Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
				processedSchedule = true;
				if (!Scheduled.CRON_DISABLED.equals(cron)) {
					TimeZone timeZone;
					if (StringUtils.hasText(zone)) {
						timeZone = StringUtils.parseTimeZoneString(zone);
					}
					else {
						timeZone = TimeZone.getDefault();
					}
					tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
				}
			}
		}
		// At this point we don't need to differentiate between initial delay set or not anymore
		if (initialDelay < 0) {
			initialDelay = 0;
		}
		// Check fixed delay
		long fixedDelay = scheduled.fixedDelay();
		if (fixedDelay >= 0) {
			Assert.isTrue(!processedSchedule, errorMessage);
			processedSchedule = true;
			tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
		}
		String fixedDelayString = scheduled.fixedDelayString();
		if (StringUtils.hasText(fixedDelayString)) {
			if (this.embeddedValueResolver != null) {
				fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);
			}
			if (StringUtils.hasLength(fixedDelayString)) {
				Assert.isTrue(!processedSchedule, errorMessage);
				processedSchedule = true;
				try {
					fixedDelay = parseDelayAsLong(fixedDelayString);
				}
				catch (RuntimeException ex) {
					throw new IllegalArgumentException(
							"Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into long");
				}
				tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
			}
		}
		// Check fixed rate
		long fixedRate = scheduled.fixedRate();
		if (fixedRate >= 0) {
			Assert.isTrue(!processedSchedule, errorMessage);
			processedSchedule = true;
			tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
		}
		String fixedRateString = scheduled.fixedRateString();
		if (StringUtils.hasText(fixedRateString)) {
			if (this.embeddedValueResolver != null) {
				fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);
			}
			if (StringUtils.hasLength(fixedRateString)) {
				Assert.isTrue(!processedSchedule, errorMessage);
				processedSchedule = true;
				try {
					fixedRate = parseDelayAsLong(fixedRateString);
				}
				catch (RuntimeException ex) {
					throw new IllegalArgumentException(
							"Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into long");
				}
				tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
			}
		}
		// Check whether we had any attribute set
		Assert.isTrue(processedSchedule, errorMessage);
		// Finally register the scheduled tasks
		synchronized (this.scheduledTasks) {
			Set<ScheduledTask> regTasks = this.scheduledTasks.computeIfAbsent(bean, key -> new LinkedHashSet<>(4));
			regTasks.addAll(tasks);
		}
	}
	catch (IllegalArgumentException ex) {
		throw new IllegalStateException(
				"Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
	}
}

org.springframework.scheduling.config.ScheduledTaskRegistrar#scheduleTasks

/**
 * Schedule all registered tasks against the underlying
 * {@linkplain #setTaskScheduler(TaskScheduler) task scheduler}.
 */
proected void scheduleTasks() {
	if (this.taskScheduler == null) {
		this.localExecutor = Executors.newSingleThreadScheduledExecutor();
		this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
	}
	if (this.triggerTasks != null) {
		for (TriggerTask task : this.triggerTasks) {
			addScheduledTask(scheduleTriggerTask(task));
		}
	}
	if (this.cronTasks != null) {
		for (CronTask task : this.cronTasks) {
			addScheduledTask(scheduleCronTask(task));
		}
	}
	if (this.fixedRateTasks != null) {
		for (IntervalTask task : this.fixedRateTasks) {
			addScheduledTask(scheduleFixedRateTask(task));
		}
	}
	if (this.fixedDelayTasks != null) {
		for (IntervalTask task : this.fixedDelayTasks) {
			addScheduledTask(scheduleFixedDelayTask(task));
		}
	}
}

使用詳解

定時任務(wù)同步/異步執(zhí)行

定時任務(wù)執(zhí)行默認(rèn)是單線程模式,會創(chuàng)建一個本地線程池,線程池大小為1。當(dāng)項目中有多個定時任務(wù)時,任務(wù)之間會相互等待,同步執(zhí)行

源碼:

// org.springframework.scheduling.config.ScheduledTaskRegistrar#scheduleTasks
if (this.taskScheduler == null) {
    this.localExecutor = Executors.newSingleThreadScheduledExecutor();
    this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}

// java.util.concurrent.Executors#newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}

代碼示例:

@Slf4j
@Component
public class RunIntervalTestScheduler {

    @Scheduled(cron = "0/1 * * * * ?")
    public void singleThreadTest1() {
        log.info("singleThreadTest1");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    }

    @Scheduled(cron = "0/1 * * * * ?")
    public void singleThreadTest2() {
        log.info("singleThreadTest2");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    }

    @Scheduled(cron = "0/1 * * * * ?")
    public void singleThreadTest3() {
        log.info("singleThreadTest3");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    }
}

執(zhí)行結(jié)果:

可以看到,默認(rèn)情況下,三個任務(wù)串行執(zhí)行,都使用pool-1-thread-1同一個線程池,并且線程只有一個

可以通過實現(xiàn)SchedulingConfigurer接口,手動創(chuàng)建線程池,配置期望的線程數(shù)量

示例代碼:

@Configuration
public class ScheduledConfig implements SchedulingConfigurer {

    /**
     * 任務(wù)執(zhí)行線程池大小
     */
    private static final int TASK_POOL_SIZE = 50;
    /**
     * 線程名
     */
    private static final String TASK_THREAD_PREFIX = "test-task-";

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        ThreadPoolTaskScheduler taskPool = new ThreadPoolTaskScheduler();
        taskPool.setPoolSize(TASK_POOL_SIZE);
        taskPool.setThreadNamePrefix(TASK_THREAD_PREFIX);
        taskPool.initialize();
        scheduledTaskRegistrar.setTaskScheduler(taskPool);
    }
}

任務(wù)執(zhí)行結(jié)果:

此時任務(wù)的執(zhí)行已經(jīng)異步化,從自定義線程池中分配線程執(zhí)行任務(wù),在實際應(yīng)用中需要考慮實際任務(wù)數(shù)量,創(chuàng)建相應(yīng)大小的線程池

fixedRate/fixedDelay區(qū)別

fixedRate是配置上一次任務(wù)執(zhí)行開始到下一次執(zhí)行開始的間隔時間,不會等待上一次任務(wù)執(zhí)行完成就會調(diào)度下一次任務(wù),將其放入等待隊列中

代碼示例:

@Slf4j
@Component
public class RunIntervalTestScheduler {

    @Scheduled(initialDelay = 1000, fixedRate = 1000)
    public void fixedRate() throws Exception {
        log.info("fixedRate run");
        TimeUnit.SECONDS.sleep(3);
    }
}

執(zhí)行結(jié)果:

任務(wù)配置的fixedRate為1s,執(zhí)行日志打印的時間間隔都是3s左右,也就是上一次執(zhí)行完成后,緊接著就執(zhí)行下一次任務(wù)

fixedDelay是配置的上一次任務(wù)執(zhí)行結(jié)束到下一次執(zhí)行開始的間隔時間,也就是說會等待上一次任務(wù)執(zhí)行結(jié)束后,延遲間隔時間,再執(zhí)行下一次任務(wù)

代碼示例:

@Slf4j
@Component
public class RunIntervalTestScheduler {

    @Scheduled(initialDelay = 1000, fixedDelay = 1000)
    public void fixedDelay() throws Exception {
        log.info("fixedDelay run");
        TimeUnit.SECONDS.sleep(3);
    }
}

執(zhí)行結(jié)果:

任務(wù)配置的fixedDelay為1s,執(zhí)行日志打印的時間間隔都是4s左右,也就是上一次執(zhí)行完成后,延遲1s后執(zhí)行下一次任務(wù)

cron表達(dá)式如果配置為類似每秒執(zhí)行、每分鐘執(zhí)行(例:0/1 * * * * ?, 每秒執(zhí)行),調(diào)度跟fixedDelay是一致的,也是在上一次任務(wù)執(zhí)行結(jié)束后,等待間隔時間

代碼示例:

@Slf4j
@Component
public class RunIntervalTestScheduler {

    @Scheduled(cron = "0/1 * * * * ?")
    public void cronRun() throws Exception{
        log.info("cron run");
        TimeUnit.SECONDS.sleep(3);
    }
}

執(zhí)行結(jié)果:

執(zhí)行日志打印的時間間隔都是4s左右,也就是上一次執(zhí)行完成后,延遲1s后執(zhí)行下一次任務(wù)

cron表達(dá)式如果配置為固定時間執(zhí)行(例:1 * * * * ?, 秒數(shù)為1時執(zhí)行),若上一次任務(wù)沒有執(zhí)行完,則不會調(diào)度本次任務(wù),跳過本次執(zhí)行,等待下一次執(zhí)行周期

代碼示例:

@Slf4j
@Component
public class RunIntervalTestScheduler {

    @Scheduled(cron = "1 * * * * ?")
    public void cronRun() throws Exception{
        log.info("cron run");
        TimeUnit.SECONDS.sleep(70);
    }
}

執(zhí)行結(jié)果:

上一次任務(wù)未執(zhí)行完畢,則跳過了本次執(zhí)行

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

相關(guān)文章

  • SpringBoot使用Jsp的示例代碼

    SpringBoot使用Jsp的示例代碼

    這篇文章主要介紹了SpringBoot使用Jsp的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • SpringBoot如何整合nacos詳解

    SpringBoot如何整合nacos詳解

    今天教各位小伙伴SpringBoot是如何整合nacos的,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)Java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-06-06
  • Java Volatile關(guān)鍵字同步機(jī)制詳解

    Java Volatile關(guān)鍵字同步機(jī)制詳解

    這篇文章主要介紹了Java Volatile關(guān)鍵字同步機(jī)制詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • springboot實現(xiàn)定時任務(wù)的四種方式小結(jié)

    springboot實現(xiàn)定時任務(wù)的四種方式小結(jié)

    本文主要介紹了springboot實現(xiàn)定時任務(wù)的四種方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Java調(diào)用linux shell腳本的方法

    Java調(diào)用linux shell腳本的方法

    這篇文章主要介紹了Java調(diào)用linux shell腳本的方法,需要的朋友可以參考下
    2015-02-02
  • Java實現(xiàn)單向鏈表的基本功能詳解

    Java實現(xiàn)單向鏈表的基本功能詳解

    這篇文章主要給大家介紹了關(guān)于Java實現(xiàn)單向鏈表基本功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • 一文掌握J(rèn)avaWeb登錄認(rèn)證

    一文掌握J(rèn)avaWeb登錄認(rèn)證

    登錄認(rèn)證是每個系統(tǒng)中必不可少的功能,通過用戶名和密碼來驗證用戶身份,JavaWeb中實現(xiàn)登錄認(rèn)證通常需要處理HTTP協(xié)議的無狀態(tài)性,涉及會話管理、令牌技術(shù)等,本文給大家介紹JavaWeb登錄認(rèn)證的相關(guān)知識,感興趣的朋友跟隨小編一起看看吧
    2024-09-09
  • Springboot整合minio實現(xiàn)文件服務(wù)的教程詳解

    Springboot整合minio實現(xiàn)文件服務(wù)的教程詳解

    這篇文章主要介紹了Springboot整合minio實現(xiàn)文件服務(wù)的教程,文中的示例代碼講解詳細(xì),對我們的工作或?qū)W習(xí)有一定幫助,需要的可以參考一下
    2022-06-06
  • 詳解SpringMVC常用注解功能及屬性

    詳解SpringMVC常用注解功能及屬性

    這篇文章主要介紹了詳解SpringMVC注解功能及屬性,文中通過詳細(xì)的示例代碼作了簡要的分析,有需要的朋友可以借鑒參考下,希望可以有所幫助
    2021-09-09
  • Java設(shè)計模式探究之觀察者模式詳解

    Java設(shè)計模式探究之觀察者模式詳解

    這篇文章主要為大家詳細(xì)介紹了JAVA的觀察者模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-08-08

最新評論

莱阳市| 澳门| 甘南县| 清新县| 九龙城区| 光山县| 柳林县| 安岳县| 玉山县| 武平县| 汪清县| 天长市| 宜城市| 都江堰市| 广宗县| 大城县| 随州市| 新竹县| 大田县| 怀远县| 左云县| 广饶县| 丽江市| 同仁县| 阿鲁科尔沁旗| 漠河县| 孟连| 三都| 福建省| 内江市| 蒙阴县| 建宁县| 昆山市| 永吉县| 邵阳县| 平乡县| 离岛区| 凌云县| 昌宁县| 泾川县| 孟津县|