This is the first day of my participation in Gwen Challenge
@Async
Implementing asynchronous calls
“Asynchronous invocation” corresponds to “synchronous invocation”. Synchronous invocation means that programs are executed in a defined order. Each line of programs must wait for the completion of the previous line of programs to be executed. Asynchronous invocation refers to the sequential execution of a program without waiting for the result of the asynchronous invocation.
As the name implies, @async is used to implement asynchrony. Methods based on @async are called asynchronous methods. When these methods are executed, they are executed in a separate thread, and the caller can proceed without waiting for it to complete.
So how do you implement asynchronous calls using @async in Spring?
If we have a Task class with three tasks that need to be executed asynchronously, we can annotate these Task methods with the @async annotation to make them asynchronous. The code is as follows:
@Component
public class AsyncTask {
private static Random random = new Random();
@Async
public void doTaskOne(a) throws Exception {
System.out.println("Start on task one.");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("Complete Task 1, Time:" + (end - start) + "毫秒");
}
@Async
public void doTaskTwo(a) throws Exception {
System.out.println("Start on task two.");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("Complete Task 2, Time:" + (end - start) + "毫秒");
}
@Async
public void doTaskThree(a) throws Exception {
System.out.println("Start on task three.");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("Complete Task 3, Time:" + (end - start) + "毫秒"); }}Copy the code
In order for the @async annotation to work, you also need to configure @enableAsync in the Spring Boot main program, as follows:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) { SpringApplication.run(Application.class, args); }}Copy the code
Then we can write a unit test to test it:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class ApplicationTests {
@Autowired
private Task task;
@Test
public void test(a) throws Exception { task.doTaskOne(); task.doTaskTwo(); task.doTaskThree(); }}Copy the code
At this point, you can run unit tests repeatedly, and you may encounter different results, such as:
- There is no task-related output
- There are partial task-specific outputs
- Out-of-order task-related output
The reason is that the doTaskOne, doTaskTwo, and doTaskThree functions are executed asynchronously. After the main program is called asynchronously, the main program does not care whether the execution of these three functions is complete. Because there is no other content to be executed, the program will automatically end, resulting in incomplete or no output task related content.
Note: Do not define @async functions as static, so asynchronous calls do not take effect.
Funture
type
So the question is, what is a Future type?
Future is an interface for canceling, querying, and retrieving the results of a specific Runnable or Callable task. If necessary, the results of the execution can be obtained through the GET method, which blocks until the task returns results.
Its interface is defined as follows:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled(a);
boolean isDone(a);
V get(a) throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Copy the code
It states five such methods:
- The cancel method is used to cancel a task, returning true on success or false on failure. The mayInterruptIfRunning parameter indicates whether ongoing tasks that have not completed are allowed to be canceled, and true indicates that ongoing tasks can be canceled. Whether mayInterruptIfRunning is true or false, this method must return false if the task is completed, or if you cancel the completed task. True if the task is executing and false if mayInterruptIfRunning is set to true. If the task has not yet been executed, true is always returned regardless of whether mayInterruptIfRunning is true or false.
- The isCancelled method indicates whether the task was cancelled successfully or returns true if the task was cancelled before it completed normally.
- The isDone method indicates whether the task is complete, and returns true if it is.
- The get() method is used to get the result of the execution, which blocks and does not return until the task is finished.
- Get (long timeout, TimeUnit Unit) is used to obtain the execution result. If no result is obtained within the specified time, null is returned.
In other words, Future provides three functions:
- Judge whether the task is completed;
- Can interrupt the task;
- Obtain the task execution results.
For the Task class above, we can also make simple changes to determine whether the above three asynchronous calls have completed. The doTaskOne() code is modified as follows:
@Async
public Future<String> doTaskOne(a) throws Exception {
System.out.println("Start on task one.");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("Complete Task 1, Time:" + (end - start) + "毫秒");
return new AsyncResult<>("Mission one complete.");
}
Copy the code
Then we’ll modify the unit test method:
@Test
public void asyncTaskTest(a) throws Exception {
long start = System.currentTimeMillis();
Future<String> task1 = asyncTask.doTaskOne();
Future<String> task2 = asyncTask.doTaskTwo();
Future<String> task3 = asyncTask.doTaskThree();
// All three tasks are completed
while(! task1.isDone() || ! task2.isDone() || ! task3.isDone()) { Thread.sleep(1000);
}
long end = System.currentTimeMillis();
System.out.println("All tasks completed, total time:" + (end - start) + "毫秒");
}
Copy the code
The following output is displayed:
Start Task 1 Start Task 2 Start Task 3 Complete Task 2 in 5,352 milliseconds complete Task 1 in 7,190 milliseconds Complete Task 3 in 7,525 milliseconds Complete all tasks in 8,004 millisecondsCopy the code
conclusion
If in a business scenario we have asynchrony and need to know the results of asynchronous method execution, then the combination of @async and Future is a good choice. In addition, Java 8 has added a new class CompletableFuture for those of you who are interested in trying it out on your own.
Links:
- Spring Boot with @async to implement asynchronous calls
- Spring Boot implements asynchronous calls using @async: Custom thread pools
- Spring Boot implements asynchronous calls using @async: Graceful shutdown of ThreadPoolTaskScheduler thread pools
- Spring Boot uses @async to implement asynchronous calls: Use Future and define timeouts
- Some columns of Completablefuture