Comment implémenter des tâches planifiées dans Java Spring
Java implémente les tâches planifiées
Dans la bibliothèque fournie avec Jdk, il existe deux façons d'implémenter des tâches planifiées, l'une est Timer
et l'autre est ScheduledThreadPoolExecutor
. Timer
,另一种是ScheduledThreadPoolExecutor
。
Timer+TimerTask
创建一个Timer
就创建了一个线程,可以用来调度TimerTask
任务
Timer
有四个构造方法,可以指定Timer
线程的名字以及是否设置为为守护线程。默认名字Timer-编号
,默认不是守护线程。
主要有三个比较重要的方法:
cancel()
:终止任务调度,取消当前调度的所有任务,正在运行的任务不受影响
purge()
:从任务队列中移除所有已经取消的任务
schedule
:开始调度任务,提供了几个重载方法:
schedule(TimerTask task, long delay)
延时执行,表示delay
毫秒后执行一次task
任务
schedule(TimerTask task, Date time)`指定时间执行,到`time`时间的时候执行一次`task
schedule(TimerTask task, long delay, long period)`延时周期执行,经过`delay`毫秒后每`period`毫秒执行一次`task
schedule(TimerTask task, Date firstTime, long period)`指定时间后周期执行,到达指定时间`firstTime`后每`period`毫秒执行一次`task
示例
public class TimerTest { public static void main(String[] args) { Timer timer = new Timer("aa"); Task task = new Task(); timer.schedule(task,new Date(),1000); } } class Task extends TimerTask{ @Override public void run() { System.out.println(new Date()); } }
输出:
Thu Jul 07 14:50:02 CST 2022
Thu Jul 07 14:50:03 CST 2022
Thu Jul 07 14:50:04 CST 2022
Thu Jul 07 14:50:05 CST 2022
…………
弊端
Timer是单一线程的,且不会抛出异常。如果定时任务产生异常,整个线程将停止,从而导致定时任务终止。
ScheduledThreadPoolExecutor
因为Timer
的缺陷,所以不建议使用Timer
,建议使用ScheduledThreadPoolExecutor
。
ScheduledThreadPoolExecutor
是Timer
的替代者,于JDK1.5引入,继承了ThreadPoolExecutor
,是基于线程池设计的定时任务类。
主要的调度方法:
schedule
只执行一次调度,(任务,延迟时间,延迟时间单位)
scheduleAtFixedRate
按固定的频率调度,如果执行时间过长,下次调度会延迟,(任务,第一次执行的延迟时间,周期,时间单位)
scheduleWithFixedDelay
延迟调度,一次任务执行完后加上延迟时间执行下一次任务,(任务,第一次执行的延迟时间,间隔时间,时间单位)
示例
public class TimerTest { public static void main(String[] args) throws Exception{ ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); scheduledExecutorService.scheduleAtFixedRate( () -> System.out.println(new Date()), 1,3, TimeUnit.SECONDS); } }
Spring定时任务
Spring定时任务主要靠@Scheduled
注解实现,corn,fixedDelay,fixedDelayString,fixedRate,fixedRateString
五个参数必须指定其一,传两个或三个都会抛出异常
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(Schedules.class) public @interface Scheduled { String CRON_DISABLED = ScheduledTaskRegistrar.CRON_DISABLED; // cron表达式 String cron() default ""; // 时区 String zone() default ""; // 从上一次调用结束到下一次调用之间的固定时间 long fixedDelay() default -1; // 和fixedDelay意思相同,只是使用字符传格式,支持占位符。例如:fixedDelayString = "${time.fixedDelay}" String fixedDelayString() default ""; // 两次调用之间固定的毫秒数(不需要等待上次任务完成) long fixedRate() default -1; // 同上,支持占位符 String fixedRateString() default ""; // 第一次执行任务前延迟的毫秒数 long initialDelay() default -1; // 同上,支持占位符 String initialDelayString() default ""; }
示例
@Component @EnableScheduling public class ScheduledTask { @Scheduled(fixedDelay = 1000) public void task(){ System.out.println("aaa"); } }
原理
项目启动ScheduledAnnotationBeanPostProcessor
的postProcessAfterInitialization()
方法扫描带有@Scheduled
注解的方法:
@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) -> // 调用processScheduled方法将定时任务的方法存放到任务队列中 scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean))); if (logger.isTraceEnabled()) { logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods); } } } return bean; }
protected void processScheduled(Scheduled scheduled, Method method, Object bean) { try { // 创建任务线程 Runnable runnable = createRunnable(bean, method); // 解析到定时任务方式的标记,解析到正确的参数后会设置为TRUE,如果在解析到了其他的参数就会抛出异常 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 解析第一次的延迟(解析initialDelay参数) long initialDelay = scheduled.initialDelay(); String initialDelayString = scheduled.initialDelayString(); if (StringUtils.hasText(initialDelayString)) { // initialDelay不能小于0 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 解析cron表达式 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)))); } } } // 第一次延迟参数小于0,默认为0 // At this point we don't need to differentiate between initial delay set or not anymore if (initialDelay < 0) { initialDelay = 0; } // Check fixed delay 解析fixedDelay参数 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 解析fixedRate参数 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()); } }
将任务解析并添加到任务队列后,交由ScheduledTaskRegistrar
类的scheduleTasks
Timer+TimerTask
Créer unTimer
crée un fil de discussion, qui peut être utilisé pour planifier des tâches TimerTask
🎜🎜
Timer
a quatre méthodes de construction, Vous pouvez spécifier le nom du thread Timer
et s'il est défini comme thread démon. Le nom par défaut est Timer-Number
, qui n'est pas un thread démon par défaut. 🎜🎜Il existe trois méthodes principales : 🎜🎜cancel()
: terminez la planification des tâches, annulez toutes les tâches actuellement planifiées et les tâches en cours d'exécution ne seront pas affectées🎜🎜purge()< /code> : Supprimez toutes les tâches annulées de la file d'attente des tâches 🎜🎜<code>schedule
: Démarrez la planification des tâches, en fournissant plusieurs méthodes de surcharge : 🎜🎜schedule(TimerTask task, long delay )
Exécution retardée, qui signifie que delay
exécute la tâche task
une fois après delay
millisecondes 🎜🎜schedule(TimerTask task, Date time)`spécifie l'heure d'exécution `task
schedule (Tâche TimerTask, long délai, longue période)` est exécuté une fois à l'heure `time`, et exécuté une fois toutes les `période` millisecondes après `delay` millisecondes. Tâche TimerTask, Date firstTime, longue période)` est exécutée périodiquement après l'heure spécifiée, et la tâche est exécutée toutes les `période` millisecondes après avoir atteint l'heure spécifiée `firstTime` `task🎜
Exempleprotected void scheduleTasks() {
if (this.taskScheduler == null) {
//获取ScheduledExecutorService对象,实际上都是使用ScheduledThreadPoolExecutor执行定时任务调度
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));
}
}
}
private void addScheduledTask(@Nullable ScheduledTask task) {
if (task != null) {
this.scheduledTasks.add(task);
}
}
Copier après la connexion🎜Sortie :
Jeudi 7 juillet 14:50:02 CST 2022
Jeudi 7 juillet 14:50:03 CST 2022
Jeudi 7 juillet 14:50:04 CST 2022
Jeu. 7 juillet 14:50:05 CST 2022
…………🎜
Inconvénients
🎜Le minuteur est monothread et ne lance pas exceptions. Si une exception se produit dans une tâche planifiée, l'ensemble du thread s'arrêtera, entraînant la fin de la tâche planifiée. 🎜ScheduledThreadPoolExecutor
🎜En raison des défauts de Timer
, il n'est pas recommandé d'utiliser Timer
. Il est recommandé d'utiliser ScheduledThreadPoolExecutorcode>. 🎜🎜<code>ScheduledThreadPoolExecutor
remplace Timer
Il a été introduit dans JDK1.5 et hérite de ThreadPoolExecutor
. Il s'agit d'une classe de tâches planifiées basée sur le thread. conception de piscine. 🎜🎜Méthode de planification principale : 🎜🎜
🎜🎜schedule
n'exécute la planification qu'une seule fois, (tâche, temps de retard, unité de temps de retard) 🎜🎜scheduleAtFixedRate
est planifié à une fréquence fixe si le temps d'exécution est trop long, il sera programmé la prochaine fois Sera retardé, (tâche, délai de première exécution, cycle, unité de temps) 🎜🎜scheduleWithFixedDelay
Planification de délai, après l'exécution d'une tâche, un délai d'exécution est ajouté la tâche suivante, (tâche, n° Délai d'une exécution, temps d'intervalle, unité de temps)🎜Exemple
rrreee🎜Tâche planifiée au printemps🎜🎜Les tâches planifiées au printemps sont principalement implémentées par @Scheduled annotation, <code>corn ,fixedDelay,fixedDelayString,fixedRate,fixedRateString
L'un des cinq paramètres doit être spécifié en passant deux ou trois lèvera une exception🎜rrreeeExemple
rrreee<. h4>Principe🎜 La méthode postProcessAfterInitialization()
de démarrage du projet ScheduledAnnotationBeanPostProcessor
analyse les méthodes avec les annotations @Scheduled
: 🎜rrreeerrreee🎜Après l'analyse et en ajoutant la tâche à la file d'attente des tâches, laissez à la méthode scheduleTasks
de la classe ScheduledTaskRegistrar
le soin d'ajouter (enregistrer) des tâches planifiées à l'environnement🎜rrreee
protected void scheduleTasks() { if (this.taskScheduler == null) { //获取ScheduledExecutorService对象,实际上都是使用ScheduledThreadPoolExecutor执行定时任务调度 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)); } } } private void addScheduledTask(@Nullable ScheduledTask task) { if (task != null) { this.scheduledTasks.add(task); } }
Jeudi 7 juillet 14:50:02 CST 2022
Jeudi 7 juillet 14:50:03 CST 2022
Jeudi 7 juillet 14:50:04 CST 2022
Jeu. 7 juillet 14:50:05 CST 2022
…………🎜
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide du nombre parfait en Java. Nous discutons ici de la définition, comment vérifier le nombre parfait en Java ?, des exemples d'implémentation de code.

Guide de Weka en Java. Nous discutons ici de l'introduction, de la façon d'utiliser Weka Java, du type de plate-forme et des avantages avec des exemples.

Guide du nombre de Smith en Java. Nous discutons ici de la définition, comment vérifier le numéro Smith en Java ? exemple avec implémentation de code.

Dans cet article, nous avons conservé les questions d'entretien Java Spring les plus posées avec leurs réponses détaillées. Pour que vous puissiez réussir l'interview.

Java 8 présente l'API Stream, fournissant un moyen puissant et expressif de traiter les collections de données. Cependant, une question courante lors de l'utilisation du flux est: comment se casser ou revenir d'une opération FOREAK? Les boucles traditionnelles permettent une interruption ou un retour précoce, mais la méthode Foreach de Stream ne prend pas directement en charge cette méthode. Cet article expliquera les raisons et explorera des méthodes alternatives pour la mise en œuvre de terminaison prématurée dans les systèmes de traitement de flux. Lire plus approfondie: Améliorations de l'API Java Stream Comprendre le flux Forach La méthode foreach est une opération terminale qui effectue une opération sur chaque élément du flux. Son intention de conception est

Guide de TimeStamp to Date en Java. Ici, nous discutons également de l'introduction et de la façon de convertir l'horodatage en date en Java avec des exemples.

Les capsules sont des figures géométriques tridimensionnelles, composées d'un cylindre et d'un hémisphère aux deux extrémités. Le volume de la capsule peut être calculé en ajoutant le volume du cylindre et le volume de l'hémisphère aux deux extrémités. Ce tutoriel discutera de la façon de calculer le volume d'une capsule donnée en Java en utilisant différentes méthodes. Formule de volume de capsule La formule du volume de la capsule est la suivante: Volume de capsule = volume cylindrique volume de deux hémisphères volume dans, R: Le rayon de l'hémisphère. H: La hauteur du cylindre (à l'exclusion de l'hémisphère). Exemple 1 entrer Rayon = 5 unités Hauteur = 10 unités Sortir Volume = 1570,8 unités cubes expliquer Calculer le volume à l'aide de la formule: Volume = π × r2 × h (4

Java est un langage de programmation populaire qui peut être appris aussi bien par les développeurs débutants que par les développeurs expérimentés. Ce didacticiel commence par les concepts de base et progresse vers des sujets avancés. Après avoir installé le kit de développement Java, vous pouvez vous entraîner à la programmation en créant un simple programme « Hello, World ! ». Une fois que vous avez compris le code, utilisez l'invite de commande pour compiler et exécuter le programme, et « Hello, World ! » s'affichera sur la console. L'apprentissage de Java commence votre parcours de programmation et, à mesure que votre maîtrise s'approfondit, vous pouvez créer des applications plus complexes.
