So implementieren Sie geplante Aufgaben in Java Spring
Java implementiert geplante Aufgaben
In der mit Jdk gelieferten Bibliothek gibt es zwei Möglichkeiten, geplante Aufgaben zu implementieren: eine ist Timer
und die andere ist 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
Durch das Erstellen einesTimer
wird ein Thread erstellt, der zum Planen von TimerTask
-Aufgaben verwendet werden kann🎜🎜
Timer
hat vier Konstruktionsmethoden: Sie können den Namen des Timer
-Threads angeben und angeben, ob er als Daemon-Thread festgelegt ist. Der Standardname ist Timer-Number
, was standardmäßig kein Daemon-Thread ist. 🎜🎜Es gibt drei Hauptmethoden: 🎜🎜cancel()
: Beenden Sie die Aufgabenplanung und stornieren Sie alle aktuell geplanten Aufgaben. Dies hat keine Auswirkungen. 🎜🎜purge()< /code>: Entfernen Sie alle abgebrochenen Aufgaben aus der Aufgabenwarteschlange. 🎜🎜<code>schedule
: Starten Sie die Planung von Aufgaben und stellen Sie mehrere Überlastungsmethoden bereit: 🎜🎜schedule(TimerTask task, long delay)
Verzögerte Ausführung, was bedeutet delay
führt die task
-Aufgabe einmal nach delay
Millisekunden aus. 🎜🎜schedule(TimerTask task, Date time)`gibt die Ausführungszeit an. „task
schedule(TimerTask task, long delay, long period)“ wird einmal zur „time“-Zeit und einmal alle „period“-Millisekunden nach „task
-schedule(TimerTask)“ ausgeführt task, Date firstTime, long period) wird regelmäßig nach der angegebenen Zeit ausgeführt, und die Aufgabe wird alle Millisekunden nach Erreichen der angegebenen Zeit „firstTime“ ausgeführt >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); } }Nach dem Login kopieren🎜Ausgabe:
Do. 07. Juli 14:50:02 CST 2022
Do. 07. Juli 14:50:03 CST 2022
Do. 07. Juli 14:50:04 CST 2022
Do. 07. Juli 14:50:05 CST 2022
…………🎜Nachteile
🎜Timer ist Single-Threaded und löst keine Ausnahmen aus . Wenn in einer geplanten Aufgabe eine Ausnahme auftritt, wird der gesamte Thread angehalten, wodurch die geplante Aufgabe beendet wird. 🎜ScheduledThreadPoolExecutor
🎜Aufgrund der Mängel vonTimer
wird die Verwendung vonTimer
nicht empfohlen. Es wird empfohlen,ScheduledThreadPoolExecutor zu verwenden. Code>. 🎜🎜<code>ScheduledThreadPoolExecutor
ist ein Ersatz fürTimer
. Es wurde in JDK1.5 eingeführt und erbtThreadPoolExecutor
. Es handelt sich um eine geplante Task-Klasse, die auf Threads basiert Pooldesign. 🎜🎜Hauptplanungsmethode: 🎜🎜🎜🎜
schedule
führt die Planung nur einmal aus (Aufgabe, Verzögerungszeit, Verzögerungszeiteinheit) 🎜🎜scheduleAtFixedRate
wird mit einer festen Häufigkeit geplant, wenn die Ausführungszeit zu lang ist. es wird das nächste Mal geplant Wird verzögert, (Aufgabe, Verzögerungszeit der ersten Ausführung, Zyklus, Zeiteinheit) 🎜🎜scheduleWithFixedDelay
Verzögerungsplanung, nachdem eine Aufgabe ausgeführt wurde, wird eine Verzögerungszeit hinzugefügt Führen Sie die nächste Aufgabe aus (Aufgabe, die erste Verzögerungszeit einer Ausführung, Intervallzeit, Zeiteinheit) annotation,corn , FixedDelay, FixedDelayString, FixedRate, FixedRateString
Einer der fünf Parameter muss angegeben werden. Die Übergabe von zwei oder drei löst eine Ausnahme aus.Beispiel
rrreeePrinzip
🎜 DiepostProcessAfterInitialization()
-Methode des ProjektstartsScheduledAnnotationBeanPostProcessor
scannt Methoden mit@Scheduled
-Annotationen: 🎜rrreeerrreee🎜After Wenn Sie die Aufgabe analysieren und zur Aufgabenwarteschlange hinzufügen, überlassen Sie es der MethodescheduleTasks
der KlasseScheduledTaskRegistrar
, geplante Aufgaben zur Umgebung hinzuzufügen (zu registrieren)🎜rrreeeDas obige ist der detaillierte Inhalt vonSo implementieren Sie geplante Aufgaben in Java Spring. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen



Leitfaden zur perfekten Zahl in Java. Hier besprechen wir die Definition, Wie prüft man die perfekte Zahl in Java?, Beispiele mit Code-Implementierung.

Leitfaden zum Zufallszahlengenerator in Java. Hier besprechen wir Funktionen in Java anhand von Beispielen und zwei verschiedene Generatoren anhand ihrer Beispiele.

Leitfaden für Weka in Java. Hier besprechen wir die Einführung, die Verwendung von Weka Java, die Art der Plattform und die Vorteile anhand von Beispielen.

Leitfaden zur Smith-Zahl in Java. Hier besprechen wir die Definition: Wie überprüft man die Smith-Nummer in Java? Beispiel mit Code-Implementierung.

In diesem Artikel haben wir die am häufigsten gestellten Fragen zu Java Spring-Interviews mit ihren detaillierten Antworten zusammengestellt. Damit Sie das Interview knacken können.

Java 8 führt die Stream -API ein und bietet eine leistungsstarke und ausdrucksstarke Möglichkeit, Datensammlungen zu verarbeiten. Eine häufige Frage bei der Verwendung von Stream lautet jedoch: Wie kann man von einem Foreach -Betrieb brechen oder zurückkehren? Herkömmliche Schleifen ermöglichen eine frühzeitige Unterbrechung oder Rückkehr, aber die Stream's foreach -Methode unterstützt diese Methode nicht direkt. In diesem Artikel werden die Gründe erläutert und alternative Methoden zur Implementierung vorzeitiger Beendigung in Strahlverarbeitungssystemen erforscht. Weitere Lektüre: Java Stream API -Verbesserungen Stream foreach verstehen Die Foreach -Methode ist ein Terminalbetrieb, der einen Vorgang für jedes Element im Stream ausführt. Seine Designabsicht ist

Anleitung zum TimeStamp to Date in Java. Hier diskutieren wir auch die Einführung und wie man Zeitstempel in Java in ein Datum konvertiert, zusammen mit Beispielen.

Java ist eine beliebte Programmiersprache, die sowohl von Anfängern als auch von erfahrenen Entwicklern erlernt werden kann. Dieses Tutorial beginnt mit grundlegenden Konzepten und geht dann weiter zu fortgeschrittenen Themen. Nach der Installation des Java Development Kit können Sie das Programmieren üben, indem Sie ein einfaches „Hello, World!“-Programm erstellen. Nachdem Sie den Code verstanden haben, verwenden Sie die Eingabeaufforderung, um das Programm zu kompilieren und auszuführen. Auf der Konsole wird „Hello, World!“ ausgegeben. Mit dem Erlernen von Java beginnt Ihre Programmierreise, und wenn Sie Ihre Kenntnisse vertiefen, können Sie komplexere Anwendungen erstellen.
