Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to implement async and scheduled tasks in Spring

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article will explain in detail how to achieve asynchronous and planned tasks in Spring. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

What is an asynchronous task in Spring?

Before we officially get to the point, for Spring, we need to understand two different concepts it implements: asynchronous tasks and scheduled tasks. Obviously, the two have one big thing in common: they both work backstage. However, there are great differences between them. Scheduling tasks is different from asynchronism in that it works exactly the same as CRON job in Linux (there are also scheduled tasks in windows). Take Chestnut, for example, there is a task that must be performed every 40 minutes, so this can be configured through a XML file or comments. Simple asynchronous tasks can be performed in the background without the need to configure the frequency of execution.

Because they are two different types of tasks, their executors are naturally different. The first one looks a bit like Java's concurrent executors (concurrency executor), and there will be a specific article on executors in Java to learn more. According to the Spring document TaskExecutor, it provides an Spring-based abstraction to handle thread pools, which can also be understood through the comments of its classes. Another abstract interface is TaskScheduler, which is used to schedule tasks at a given point in time in the future and execute them once or periodically.

In the process of analyzing the source code, it is found that another interesting point is the trigger. It has two types: CronTrigger or PeriodTrigger. The first simulates the behavior of the CRON task. So we can submit a task for execution at an exact point in time in the future. Another trigger can be used to perform tasks on a regular basis.

Asynchronous task class of Spring

Let's start with the analysis of the org.springframework.core.task.TaskExecutor class. You will find that it is too simple, it is an interface that extends the Executor interface of Java. The only way to do this is to perform a task that uses the Runnable type in the parameters.

Package org.springframework.core.task;import java.util.concurrent.Executor;/** * Simple task executor interface that abstracts the execution * of a {@ link Runnable}. * *

Implementations can use all sorts of different execution strategies, * such as: synchronous, asynchronous, using a thread pool, and more. * *

Equivalent to JDK 1.5s {@ link java.util.concurrent.Executor} * interface; extending it now in Spring 3.0,so that clients may declare * a dependency on an Executor and receive any TaskExecutor implementation. * This interface remains separate from the standard Executor interface * mainly for backwards compatibility with JDK 1.4 in Spring 2.x. * * @ author Juergen Hoeller * @ since 2.0 * @ see java.util.concurrent.Executor * / @ FunctionalInterfacepublic interface TaskExecutor extends Executor {/ * Execute the given {@ code task}. *

The call might return immediately if the implementation uses * an asynchronous execution strategy, or might block in the case * of synchronous execution. * @ param task the {@ code Runnable} to execute (never {@ code null}) * @ throws TaskRejectedException if the given task was not accepted * / @ Override void execute (Runnable task);}

Relatively speaking, the org.springframework.scheduling.TaskScheduler interface is a little complicated. It defines a set of methods with names starting with schedule that allow us to define tasks to be performed in the future. All schedule* methods return java.util.concurrent.ScheduledFuture instances. The scheduleAtFixedRate method is further enriched in Spring5. In fact, ScheduledFuture scheduleAtFixedRate (Runnable task, long period) is finally called.

Public interface TaskScheduler {/ * * Schedule the given {@ link Runnable}, invoking it whenever the trigger * indicates a next execution time. *

Execution will end once the scheduler shuts down or the returned * {@ link ScheduledFuture} gets cancelled. * @ param task the Runnable to execute whenever the trigger fires * @ param trigger an implementation of the {@ link Trigger} interface, * e.g. A {@ link org.springframework.scheduling.support.CronTrigger} object * wrapping a cron expression * @ return a {@ link ScheduledFuture} representing pending completion of the task * or {@ code null} if the given Trigger object never fires (i.e. Returns * {@ code null} from {@ link Trigger#nextExecutionTime}) * @ throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * for internal reasons (e.g. A pool overload handling policy or a pool shutdown in progress) * @ see org.springframework.scheduling.support.CronTrigger * / @ Nullable ScheduledFuture schedule (Runnable task, Trigger trigger) / * * Schedule the given {@ link Runnable}, invoking it at the specified execution time. *

Execution will end once the scheduler shuts down or the returned * {@ link ScheduledFuture} gets cancelled. * @ param task the Runnable to execute whenever the trigger fires * @ param startTime the desired execution time for the task * (if this is in the past, the task will be executed immediately, i.e. As soon as possible) * @ return a {@ link ScheduledFuture} representing pending completion of the task * @ throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * for internal reasons (e.g. A pool overload handling policy or a pool shutdown in progress) * uses the default implementation, which is worth learning. There can also be private implementations in Java9. From here we can do it only through * one interface you implement, I will implement other corresponding functions by default, and finally call your custom implemented interface to make the interface function more * clear at a glance * @ since 5.0* @ see # schedule (Runnable, Date) * / default ScheduledFuture schedule (Runnable task, Instant startTime) {return schedule (task, Date.from (startTime)) } / * Schedule the given {@ link Runnable}, invoking it at the specified execution time. *

Execution will end once the scheduler shuts down or the returned * {@ link ScheduledFuture} gets cancelled. * @ param task the Runnable to execute whenever the trigger fires * @ param startTime the desired execution time for the task * (if this is in the past, the task will be executed immediately, i.e. As soon as possible) * @ return a {@ link ScheduledFuture} representing pending completion of the task * @ throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * for internal reasons (e.g. A pool overload handling policy or a pool shutdown in progress) * / ScheduledFuture schedule (Runnable task, Date startTime) ... / * * Schedule the given {@ link Runnable}, invoking it at the specified execution time * and subsequently with the given period. *

Execution will end once the scheduler shuts down or the returned * {@ link ScheduledFuture} gets cancelled. * @ param task the Runnable to execute whenever the trigger fires * @ param startTime the desired first execution time for the task * (if this is in the past, the task will be executed immediately I.e. As soon as possible) * @ param period the interval between successive executions of the task * @ return a {@ link ScheduledFuture} representing pending completion of the task * @ throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * for internal reasons (e.g. A pool overload handling policy or a pool shutdown in progress) * @ since 5.0* @ see # scheduleAtFixedRate (Runnable, Date, long) * / default ScheduledFuture scheduleAtFixedRate (Runnable task, Instant startTime, Duration period) {return scheduleAtFixedRate (task, Date.from (startTime) Period.toMillis () } / * Schedule the given {@ link Runnable}, invoking it at the specified execution time * and subsequently with the given period. *

Execution will end once the scheduler shuts down or the returned * {@ link ScheduledFuture} gets cancelled. * @ param task the Runnable to execute whenever the trigger fires * @ param startTime the desired first execution time for the task * (if this is in the past, the task will be executed immediately I.e. As soon as possible) * @ param period the interval between successive executions of the task (in milliseconds) * @ return a {@ link ScheduledFuture} representing pending completion of the task * @ throws org.springframework.core.task.TaskRejectedException if the given task was not accepted * for internal reasons (e.g. A pool overload handling policy or a pool shutdown in progress) * / ScheduledFuture scheduleAtFixedRate (Runnable task, Date startTime, long period) ...}

I mentioned two trigger components, both of which implement the org.springframework.scheduling.Trigger interface. Here, we only need to focus on one method, nextExecutionTime, which defines the execution time of the next triggered task. Its two implementations, CronTrigger and PeriodicTrigger, use org.springframework.scheduling.TriggerContext to store information, so we can easily get the last execution time (lastScheduledExecutionTime) of a task, the last completion time (lastCompletionTime) of a given task, or the last actual execution time (lastActualExecutionTime). Next, let's simply learn about these things by reading the source code. Org.springframework.scheduling.concurrent.ConcurrentTaskScheduler contains a private class, EnterpriseConcurrentTriggerScheduler. In this class, we can find the schedule method:

Public ScheduledFuture schedule (Runnable task, final Trigger trigger) {ManagedScheduledExecutorService executor = (ManagedScheduledExecutorService) scheduledExecutor; return executor.schedule (task, new javax.enterprise.concurrent.Trigger () {@ Override public Date getNextRunTime (LastExecution le, Date taskScheduledTime) {return trigger.nextExecutionTime (le! = null? New SimpleTriggerContext (le.getScheduledStart (), le.getRunStart (), le.getRunEnd ()): new SimpleTriggerContext ();} @ Override public boolean skipRun (LastExecution lastExecution, Date scheduledRunTime) {return false;}});}

SimpleTriggerContext can see a lot from its name because it implements the TriggerContext interface.

/ * * Simple data holder implementation of the {@ link TriggerContext} interface. * * @ author Juergen Hoeller * @ since 3.0 * / public class SimpleTriggerContext implements TriggerContext {@ Nullable private volatile Date lastScheduledExecutionTime; @ Nullable private volatile Date lastActualExecutionTime; @ Nullable private volatile Date lastCompletionTime;... / * Create a SimpleTriggerContext with the given time values. * @ param lastScheduledExecutionTime last scheduled execution time * @ param lastActualExecutionTime last actual execution time * @ param lastCompletionTime last completion time * / public SimpleTriggerContext (Date lastScheduledExecutionTime, Date lastActualExecutionTime, Date lastCompletionTime) {this.lastScheduledExecutionTime = lastScheduledExecutionTime; this.lastActualExecutionTime = lastActualExecutionTime; this.lastCompletionTime = lastCompletionTime;}.}

As you can see, the time value set in the constructor comes from the implementation of javax.enterprise.concurrent.LastExecution, where:

GetScheduledStart: returns the time when the task started last time. It corresponds to the lastScheduledExecutionTime property of TriggerContext.

GetRunStart: returns the time when a given task starts running. In TriggerContext, it corresponds to lastActualExecutionTime.

GetRunEnd: returns when the task terminates. It is used to set up lastCompletionTime in TriggerContext.

Another important class in Spring scheduling and asynchronous execution is org.springframework.core.task.support.TaskExecutorAdapter. It is an adapter that uses java.util.concurrent.Executor as the basic executor of Spring (described as a bit of a mouthful, as you can see below), and TaskExecutor has been described before. In fact, it references Java's ExecutorService, which also inherits the Executor interface. This reference is used to complete all submitted tasks.

/ * Adapter that takes a JDK {@ code java.util.concurrent.Executor} and * exposes a Spring {@ link org.springframework.core.task.TaskExecutor} for it. * Also detects an extended {@ code java.util.concurrent.ExecutorService explains the above instructions}, adapting * the {@ link org.springframework.core.task.AsyncTaskExecutor} interface accordingly. * * @ author Juergen Hoeller * @ since 3.0 * @ see java.util.concurrent.Executor * @ see java.util.concurrent.ExecutorService * @ see java.util.concurrent.Executors * / public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {private final Executor concurrentExecutor; @ Nullable private TaskDecorator taskDecorator;... / * Create a new TaskExecutorAdapter, * using the given JDK concurrent executor. * @ param concurrentExecutor the JDK concurrent executor to delegate to * / public TaskExecutorAdapter (Executor concurrentExecutor) {Assert.notNull (concurrentExecutor, "Executor must not be null"); this.concurrentExecutor = concurrentExecutor;} / * Delegates to the specified JDK concurrent executor. * @ see java.util.concurrent.Executor#execute (Runnable) * / @ Override public void execute (Runnable task) {try {doExecute (this.concurrentExecutor, this.taskDecorator, task);} catch (RejectedExecutionException ex) {throw new TaskRejectedException ("Executor [" + this.concurrentExecutor + "] did not accept task:" + task, ex);} @ Override public void execute (Runnable task, long startTimeout) {execute (task) } @ Override public Future submit (Runnable task) {try {if (this.taskDecorator = = null & & this.concurrentExecutor instanceof ExecutorService) {return ((ExecutorService) this.concurrentExecutor). Submit (task);} else {FutureTask future = new FutureTask (task, null); doExecute (this.concurrentExecutor, this.taskDecorator, future); return future;}} catch (RejectedExecutionException ex) {throw new TaskRejectedException ("Executor [" + this.concurrentExecutor + "] did not accept task:" + task, ex) }}...}

Configure async and scheduled tasks in Spring

Let's implement asynchronous tasks in the form of code. First, we need to enable the configuration through annotations. Its XML configuration is as follows:

You can activate both by adding @ EnableScheduling and @ EnableAsync annotations to the configuration class (with @ Configuration annotations). When we're done, we can start implementing scheduling and asynchronous tasks. To schedule tasks, we can use the @ Scheduled annotation. We can find this annotation in the org.springframework.scheduling.annotation package. It contains the following attributes:

Cron: configure annotated tasks that need to be started using the CRON style (the style of Linux configuring scheduled tasks).

Zone: the time zone in which you want to resolve the CRON expression.

FixedDelay or fixedDelayString: execute the task after a fixed delay. That is, the task will be executed after the fixed period of time between the end of the last call and the start of the next call.

FixedRate or fixedRateString: calls to methods annotated with fixedRate will occur for a fixed period of time (for example, every 10 seconds), regardless of the execution lifecycle (start, end).

InitialDelay or initialDelayString: delays the first execution of the scheduling method. Note that all values (fixedDelay, fixedRate, initialDelay) must be expressed in milliseconds. It is important to keep in mind that the method annotated with @ Scheduled does not accept any parameters and does not return anything (void). If there is a return value, the return value will be ignored and useless. The timed task method is managed by the container, not called by the caller at run time. They are parsed by org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor and include the following methods to refuse to execute all incorrectly defined functions:

Protected void processScheduled (Scheduled scheduled, Method method, Object bean) {try {Assert.isTrue (method.getParameterCount () = = 0, "Only no-arg methods may be annotated with @ Scheduled") / * * in the previous version, the return value was rejected directly, but in the Spring 4.3 Spring5 version, it is not so strict * Assert.isTrue (void.class.equals (method.getReturnType ()), * "Only void-returning methods may be annotated with @ Scheduled"); * * / /. / * comments are very important * An annotation that marks a method to be scheduled. Exactly one of * the {@ link # cron ()}, {@ link # fixedDelay ()}, or {@ link # fixedRate ()} * attributes must be specified. * *

The annotated method must expect no arguments. It will typically have * a {@ code void} return type; if not, the returned value will be ignored * when called through the scheduler. * *

Processing of {@ code @ Scheduled} annotations is performed by * registering a {@ link ScheduledAnnotationBeanPostProcessor}. This can be * done manually or, more conveniently, through the {@ code} * element or @ {@ link EnableScheduling} annotation. * *

This annotation may be used as a meta-annotation to create custom * composed annotations with attribute overrides. * * @ author Mark Fisher * @ author Dave Syer * @ author Chris Beams * @ since 3.0 * @ see EnableScheduling * @ see ScheduledAnnotationBeanPostProcessor * @ see Schedules * / @ Target ({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @ Retention (RetentionPolicy.RUNTIME) @ Documented@Repeatable (Schedules.class) public @ interface Scheduled {...}

Mark a method or class with the @ Async annotation (by marking a class, we automatically mark all its methods asynchronously). Unlike @ Scheduled, asynchronous tasks can accept parameters and may return something.

Write a Demo that performs asynchronous tasks in Spring

With the above knowledge, we can write asynchronous and scheduled tasks. We will show it through test cases. Let's start by testing different task executors (task executors):

RunWith (SpringJUnit4ClassRunner.class) @ ContextConfiguration (locations= {"classpath:applicationContext-test.xml"}) @ WebAppConfigurationpublic class TaskExecutorsTest {@ Test public void simpeAsync () throws InterruptedException {/ * SimpleAsyncTaskExecutor creates new Thread for every task and executes it asynchronously. The threads aren't reused as in * native Java's thread pools. * * The number of concurrently executed threads can be specified through concurrencyLimit bean property * (concurrencyLimit XML attribute) Here it's more simple to invoke setConcurrencyLimit method. * Here the tasks will be executed by 2 simultaneous threads. Without specifying this value, * the number of executed threads will be indefinite. * * You can observe that only 2 tasks are executed at a given time-even if 3 are submitted to execution (lines 40-42). * * / SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor ("thread_name_prefix_"); executor.setConcurrencyLimit (2); executor.execute (new SimpleTask ("SimpleAsyncTask-1", Counters.simpleAsyncTask, 1000)); executor.execute (new SimpleTask ("SimpleAsyncTask-2", Counters.simpleAsyncTask, 1000)); Thread.sleep (1050); assertTrue ("2 threads should be terminated, but" + Counters.simpleAsyncTask.getNb () + "were instead", Counters.simpleAsyncTask.getNb () = 2) Executor.execute (new SimpleTask ("SimpleAsyncTask-3", Counters.simpleAsyncTask, 1000); executor.execute (new SimpleTask ("SimpleAsyncTask-4", Counters.simpleAsyncTask, 1000)); executor.execute (new SimpleTask ("SimpleAsyncTask-5", Counters.simpleAsyncTask, 2000)); Thread.sleep (1050); assertTrue ("4 threads should be terminated, but" + Counters.simpleAsyncTask.getNb () + "were instead", Counters.simpleAsyncTask.getNb () = = 4) Executor.execute (new SimpleTask ("SimpleAsyncTask-6", Counters.simpleAsyncTask, 1000); Thread.sleep (1050); assertTrue ("6 threads should be terminated, but" + Counters.simpleAsyncTask.getNb () + "were instead", Counters.simpleAsyncTask.getNb () = = 6);} @ Test public void syncTaskTest () {/ * SyncTask works almost as Java's CountDownLatch. In fact, this executor is synchronous with the calling thread. In our case, * SyncTaskExecutor tasks will be synchronous with JUnit thread. It means that the testing thread will sleep 5 * seconds after executing the third task ('SyncTask-3'). To prove that, we check if the total execution time is ~ 5 seconds. * / long start = System.currentTimeMillis (); SyncTaskExecutor executor = new SyncTaskExecutor (); executor.execute (new SimpleTask ("SyncTask-1", Counters.syncTask, 0)); executor.execute (new SimpleTask ("SyncTask-2", Counters.syncTask, 0)); executor.execute (new SimpleTask ("SyncTask-3", Counters.syncTask, 0)); executor.execute (new SimpleTask ("SyncTask-4", Counters.syncTask, 5000)); executor.execute (new SimpleTask ("SyncTask-5", Counters.syncTask, 0)) Long end = System.currentTimeMillis (); int execTime = Math.round ((end-start) / 1000); assertTrue ("Execution time should be 5 seconds but was" + execTime+ "seconds", execTime = = 5);} @ Test public void threadPoolTest () throws InterruptedException {/ * This executor can be used to expose Java's native ThreadPoolExecutor as Spring bean, with the * possibility to set core pool size, max pool size and queue capacity through bean properties. * * It works exactly as ThreadPoolExecutor from java.util.concurrent package. It means that our pool starts * with 2 threads (core pool size) and can be growth until 3 (max pool size). * In additionally, 1 task can be stored in the queue. This task will be treated * as soon as one from 3 threads ends to execute provided task. In our case, we try to execute 5 tasks * in 3 places pool and 1 place queue. So the 5th task should be rejected and TaskRejectedException should be thrown. * / ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor (); executor.setCorePoolSize (2); executor.setMaxPoolSize (3); executor.setQueueCapacity (1); executor.initialize (); executor.execute (new SimpleTask ("ThreadPoolTask-1", Counters.threadPool, 1000)); executor.execute (new SimpleTask ("ThreadPoolTask-2", Counters.threadPool, 1000)); executor.execute (new SimpleTask ("ThreadPoolTask-3", Counters.threadPool, 1000)); executor.execute ("ThreadPoolTask-4", Counters.threadPool, 1000)) Boolean wasTre = false; try {executor.execute (new SimpleTask ("ThreadPoolTask-5", Counters.threadPool, 1000);} catch (TaskRejectedException tre) {wasTre = true;} assertTrue ("The last task should throw a TaskRejectedException but it wasn't", wasTre); Thread.sleep (3000); assertTrue ("4 tasks should be terminated, but" + Counters.threadPool.getNb () + "were instead", Counters.threadPool.getNb () = = 4);}} class SimpleTask implements Runnable {private String name; private Counters counter Private int sleepTime; public SimpleTask (String name, Counters counter, int sleepTime) {this.name = name; this.counter = counter; this.sleepTime = sleepTime;} @ Override public void run () {try {Thread.sleep (this.sleepTime);} catch (InterruptedException e) {e.printStackTrace ();} this.counter.increment (); System.out.println ("Running task'" + this.name+ "'in Thread" + Thread.currentThread (). GetName ()) } @ Override public String toString () {return "Task {" + this.name+ "};} enum Counters {simpleAsyncTask (0), syncTask (0), threadPool (0); private int nb; public int getNb () {return this.nb;} public synchronized void increment () {this.nb++;} private Counters (int n) {this.nb = n;}}

In the past, we could have more actuators to use (SimpleThreadPoolTaskExecutor,TimerTaskExecutor these are 2.x 3.x antiquities). But all of them are deprecated and replaced by local Java actuators as the first choice of Spring. Look at the results of the output:

Running task 'SimpleAsyncTask-1' in Thread thread_name_prefix_1Running task' SimpleAsyncTask-2' in Thread thread_name_prefix_2Running task 'SimpleAsyncTask-3' in Thread thread_name_prefix_3Running task' SimpleAsyncTask-4' in Thread thread_name_prefix_4Running task 'SimpleAsyncTask-5' in Thread thread_name_prefix_5Running task' SimpleAsyncTask-6' in Thread thread_name_prefix_6Running task 'SyncTask-1 'in Thread mainRunning task 'SyncTask-2' in Thread mainRunning task' SyncTask-3' in Thread mainRunning task 'SyncTask-4' in Thread mainRunning task' SyncTask-5' in Thread mainRunning task 'ThreadPoolTask-2' in Thread ThreadPoolTaskExecutor-2Running task' ThreadPoolTask-1' in Thread ThreadPoolTaskExecutor-1Running task 'ThreadPoolTask-4' in Thread ThreadPoolTaskExecutor-3Running task' ThreadPoolTask-3' in Thread ThreadPoolTaskExecutor-2

From this we can infer that the first test creates a new thread for each task. We can see the difference by using different thread names. The second, the synchronous executor, should perform the task in the called thread. Here you can see that 'main' is the name of the main thread, and its main thread calls to synchronize all tasks. The last example involves a thread pool that can create up to three threads. As you can see from the results, they do have only three creation threads.

Now, we'll write some unit tests to look at @ Async and @ Scheduled implementations.

@ RunWith (SpringJUnit4ClassRunner.class) @ ContextConfiguration (locations= {"classpath:applicationContext-test.xml"}) @ WebAppConfigurationpublic class AnnotationTest {@ Autowired private GenericApplicationContext context; @ Test public void testScheduled () throws InterruptedException {System.out.println ("Start sleeping"); Thread.sleep (6000); System.out.println ("Wake up!"); TestScheduledTask scheduledTask = (TestScheduledTask) context.getBean ("testScheduledTask"); / * * Test fixed delay. It's executed every 6 seconds. The first execution is registered after application's context start. * * / assertTrue ("Scheduled task should be executed 2 times (1 before sleep in this method, 1 after the sleep), but was" + scheduledTask.getFixedDelayCounter (), scheduledTask.getFixedDelayCounter () = = 2); / * * Test fixed rate. It's executed every 6 seconds. The first execution is registered after application's context start. * Unlike fixed delay, a fixed rate configuration executes one task with specified time. For example, it will execute on * 6 seconds delayed task at 10:30:30, 10:30:36, 10:30:42 and so on-even if the task 10:30:30 taken 30 seconds to * be terminated. In teh case of fixed delay, if the first task takes 30 seconds, the next will be executed 6 seconds * after the first one, so the execution flow will be: 10:30:30, 10:31:06, 10:31:12. * * / assertTrue ("Scheduled task should be executed 2 times (1 before sleep in this method, 1 after the sleep), but was" + scheduledTask.getFixedRateCounter (), scheduledTask.getFixedRateCounter () = 2); / * * Test fixed rate with initial delay attribute. The initialDelay attribute is set to 6 seconds. It causes that * scheduled method is executed 6 seconds after application's context start. In our case, it should be executed * only once because of previous Thread.sleep (6000) invocation. * * / assertTrue ("Scheduled task should be executed 1 time (0 before sleep in this method, 1 after the sleep), but was" + scheduledTask.getInitialDelayCounter (), scheduledTask.getInitialDelayCounter () = = 1); / * * Test cron scheduled task. Cron is scheduled to be executed every 6 seconds. It's executed only once, * so we can deduce that it's not invoked directly before applications * context start, but only after configured time (6 seconds in our case). * * / assertTrue ("Scheduled task should be executed 1 time (0 before sleep in this method, 1 after the sleep), but was" + scheduledTask.getCronCounter (), scheduledTask.getCronCounter () = = 1);} @ Test public void testAsyc () throws InterruptedException {/ * * To test @ Async annotation, we can create a bean in-the-fly. AsyncCounter bean is a * simple counter which value should be equals to 2 at the end of the test. A supplementary test * concerns threads which execute both of AsyncCounter methods: one which * isn't annotated with @ Async and another one which is annotated with it. For the first one, invoking * thread should have the same name as the main thread. For annotated method, it can't be executed in * the main thread. It must be executed asynchrounously in a new thread. * * / context.registerBeanDefinition ("asyncCounter", new RootBeanDefinition (AsyncCounter.class)); String currentName = Thread.currentThread (). GetName (); AsyncCounter asyncCounter = context.getBean ("asyncCounter", AsyncCounter.class); asyncCounter.incrementNormal (); assertTrue ("Thread executing normal increment should be the same as JUnit thread but it wasn't (expected'" + currentName+ ", got'" + asyncCounter.getNormalThreadName () + ")", asyncCounter.getNormalThreadName () .equals (currentName); asyncCounter.incrementAsync () / / sleep 50ms and give some time to AsyncCounter to update asyncThreadName value Thread.sleep (50); assertFalse ("Thread executing @ Async increment shouldn't be the same as JUnit thread but it wasn (JUnit thread'" + currentName+ "', @ Async thread'" + asyncCounter.getAsyncThreadName () + "')", asyncCounter.getAsyncThreadName (). Equals (currentName); System.out.println ("Main thread execution's name:" + currentName) System.out.println ("AsyncCounter normal increment thread execution's name:" + asyncCounter.getNormalThreadName ()); System.out.println ("AsyncCounter @ Async increment thread execution's name:" + asyncCounter.getAsyncThreadName ()); assertTrue ("Counter should be 2, but was" + asyncCounter.getCounter (), asyncCounter.getCounter () = = 2);}} class AsyncCounter {private int counter = 0; private String normalThreadName; private String asyncThreadName; public void incrementNormal () {normalThreadName = Thread.currentThread (). GetName (); this.counter++ @ Async public void incrementAsync () {asyncThreadName = Thread.currentThread () .getName (); this.counter++;} public String getAsyncThreadName () {return asyncThreadName;} public String getNormalThreadName () {return normalThreadName;} public int getCounter () {return this.counter;}}

In addition, we need to create a new configuration file and a class that contains scheduled task methods:

/ scheduled methods after, all are executed every 6 seconds (scheduledAtFixedRate and scheduledAtFixedDelay start to execute at// application context start, two other methods begin 6 seconds after application's context start) @ Componentpublic class TestScheduledTask {private int fixedRateCounter = 0; private int fixedDelayCounter = 0; private int initialDelayCounter = 0; private int cronCounter = 0; @ Scheduled (fixedRate = 6000) public void scheduledAtFixedRate () {System.out.println ("Increment at fixed rate"); fixedRateCounter++ } @ Scheduled (fixedDelay = 6000) public void scheduledAtFixedDelay () {System.out.println ("Incrementing at fixed delay"); fixedDelayCounter++;} @ Scheduled (fixedDelay = 6000, initialDelay = 6000) public void scheduledWithInitialDelay () {System.out.println ("Incrementing with initial delay"); initialDelayCounter++;} @ Scheduled (cron = "* / 6 *") public void scheduledWithCron () {System.out.println ("Incrementing with cron"); cronCounter++ } public int getFixedRateCounter () {return this.fixedRateCounter;} public int getFixedDelayCounter () {return this.fixedDelayCounter;} public int getInitialDelayCounter () {return this.initialDelayCounter;} public int getCronCounter () {return this.cronCounter;}}

The output of the test:

Increment at fixed rate Incrementing at fixed delayStart sleeping Incrementing with cron Incrementing with initial delay Increment at fixed rate Incrementing at fixed delayWake up! Main thread execution's name: mainAsyncCounter normal increment thread execution's name: mainAsyncCounter @ Async increment thread execution's name: taskExecutor-1 's article on "how to implement async and scheduled tasks in Spring" ends here. I hope the above content can be helpful to you, so that you can learn more knowledge, if you think the article is good. Please share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report