Heim > Java > javaLernprogramm > So implementieren Sie geplante Aufgaben in Java Spring

So implementieren Sie geplante Aufgaben in Java Spring

PHPz
Freigeben: 2023-05-24 13:28:23
nach vorne
1270 Leute haben es durchsucht

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任务

So implementieren Sie geplante Aufgaben in Java Spring

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());
    }
}
Nach dem Login kopieren

输出:
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

ScheduledThreadPoolExecutorTimer的替代者,于JDK1.5引入,继承了ThreadPoolExecutor,是基于线程池设计的定时任务类。

主要的调度方法:

So implementieren Sie geplante Aufgaben in Java Spring

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);
    }
}
Nach dem Login kopieren

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 "";
}
Nach dem Login kopieren

示例

@Component
@EnableScheduling
public class ScheduledTask {
    @Scheduled(fixedDelay = 1000)
    public void task(){
        System.out.println("aaa");
    }
}
Nach dem Login kopieren

原理

项目启动ScheduledAnnotationBeanPostProcessorpostProcessAfterInitialization()方法扫描带有@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 &#39;" + beanName +
							"&#39;: " + annotatedMethods);
				}
			}
		}
		return bean;
	}
Nach dem Login kopieren
	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 &#39;cron&#39;, &#39;fixedDelay(String)&#39;, or &#39;fixedRate(String)&#39; 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 &#39;initialDelay&#39; or &#39;initialDelayString&#39;, 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, "&#39;initialDelay&#39; 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&#39;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 &#39;" + method.getName() + "&#39;: " + ex.getMessage());
		}
	}
Nach dem Login kopieren

将任务解析并添加到任务队列后,交由ScheduledTaskRegistrar类的scheduleTasks

Timer+TimerTask

Durch das Erstellen eines Timer wird ein Thread erstellt, der zum Planen von TimerTask-Aufgaben verwendet werden kann🎜🎜Wie implementiert Java Spring geplante Aufgaben🎜🎜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 von Timer wird die Verwendung von Timer nicht empfohlen. Es wird empfohlen, ScheduledThreadPoolExecutor zu verwenden. Code>. 🎜🎜<code>ScheduledThreadPoolExecutor ist ein Ersatz für Timer. Es wurde in JDK1.5 eingeführt und erbt ThreadPoolExecutor. Es handelt sich um eine geplante Task-Klasse, die auf Threads basiert Pooldesign. 🎜🎜Hauptplanungsmethode: 🎜🎜Wie implementiert Java Spring geplante Aufgaben 🎜🎜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, FixedRateStringEiner der fünf Parameter muss angegeben werden. Die Übergabe von zwei oder drei löst eine Ausnahme aus.

Beispiel

rrreee

Prinzip

🎜 Die postProcessAfterInitialization()-Methode des Projektstarts ScheduledAnnotationBeanPostProcessor scannt Methoden mit @Scheduled-Annotationen: 🎜rrreeerrreee🎜After Wenn Sie die Aufgabe analysieren und zur Aufgabenwarteschlange hinzufügen, überlassen Sie es der Methode scheduleTasks der Klasse ScheduledTaskRegistrar, geplante Aufgaben zur Umgebung hinzuzufügen (zu registrieren)🎜rrreee

Das 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!

Verwandte Etiketten:
Quelle:yisu.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage