Today we will learn how to do asynchronous programming in Spring. Web server processes the request, as we all know, the request of the thread is to get the thread pool, this also is not difficult to explain, because when web request concurrency is very big, how to create a request came in a processing threads, due to create a thread and thread context switch overhead is relatively large, the web server will eventually face collapse. In addition, the processing thread created by the Web server executes synchronously from start to finish by default. That is, if processing thread A is responsible for processing request B, processing thread A cannot leave to process other requests until B returns, which greatly limits the concurrent processing capacity of the Web server.

So thread pools solve the problem of thread recycling, but what about synchronous processing requests? The answer is asynchronous processing. What is asynchronous processing? Asynchronous processing mainly allows thread A to be free to continue processing other requests before the processing of B request is completed. So we can do this by restarting A thread C within thread A to perform the task, and let thread A directly return to the Web server to continue receiving incoming requests.

Before I begin, let me distinguish two concepts:

  • 1. Handle threads

    Processing threads are web servers that handle user requests and are managed by thread pools

  • 2. Asynchronous threads

    Asynchronous threads are user-defined threads that can be managed by thread pools

Spring provides support for asynchronous tasks. WebAsyncTask can be used to implement asynchronous tasks. At the same time, we can also set corresponding callback processing for asynchronous tasks, such as how to deal with tasks timeout and exceptions thrown. Asynchronous tasks are often useful when we want to delegate an operation that might take a long time to an asynchronous thread, or when an order is paid, we can start the asynchronous task to query the payment result of the order.

I. Normal asynchronous tasks

For the sake of illustration, the execution of asynchronous tasks is simulated using Thread.sleep(long). Now assume that the user requests the following interface:

http://localhost:7000/demo/getUserWithNoThing.json

The asynchronous task interface is defined as follows:

@requestMapping (value =); /* RequestMapping(value =)"getUserWithNoThing.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithNoThing() {// Print the processing thread name system.err.println ()"The main Thread name is "+ Thread.currentThread().getName()); WebAsyncTask<String> Task1 = new WebAsyncTask<String>(10 * 1000L, () -> {system.err.println ()"The first Thread name is "+ Thread.currentThread().getName()); Thread.sleep(5 * 1000L);return "Mission 1 was successfully executed! No exceptions are thrown!"; }); Task1.oncompletion (() -> {system.err.println ()"Mission 1 completed!");
    });
    
    System.err.println("Task1 moves on to other things!");
    return task1;
}
Copy the code

The console prints the following:

The main Thread name is http-nio-7000-exec-1 task1. The first Thread name is MvcAsync1Copy the code

The browser result is as follows:

2. Throw abnormal asynchronous tasks

Interface call: http://localhost:7000/demo/getUserWithError.json

/** * test asynchronous tasks with error * @return
 */
@RequestMapping(value = "getUserWithError.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithError() {
	System.err.println("The main Thread name is "+ Thread.currentThread().getName()); WebAsyncTask<String> Task3 = new WebAsyncTask<String>(10 * 1000L, () -> {system.err.println ()"The second Thread name is "+ Thread.currentThread().getName()); Int num = 9/0; System.err.println(num);return ""; }); Task3.onerror (() -> {system.err.println ()"= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =" + Thread.currentThread().getName()
				+ "= = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
		System.err.println("Error in mission 3!");
		return ""; }); Task3. onCompletion(() -> {system.err.println ()"Mission 3 is complete!");
	});

	System.err.println("Task3 moves on to other things!");
	return task3;
}
Copy the code

The console output is as follows:

The main Thread name is http-nio-7000-exec-1 task3. The second Thread name is MvcAsync1 2018-06-15 09:40:13.538 ERROR 9168 -- [NIO-7000-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service()for servlet [dispatcherServlet] threw exception

java.lang.ArithmeticException: / by zero
	at com.example.demo.controller.GetUserInfoController.lambdaA $5(GetUserInfoController.java:93) ~[classes/:na]
	at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$4(WebAsyncManager. Java: 317) ~ [spring - web - 5.0.6. The jar: 5.0.6. RELEASE] at Java. Util. Concurrent. The Executors$RunnableAdapterCall (Executors. Java: 511) ~ [na: 1.8.0 comes with _161] at Java. Util. Concurrent. FutureTask. Run (FutureTask. Java: 266) ~ [na: 1.8.0 comes with _161] the at Java.lang.Thread.run(thread.java :748) [na:1.8.0_161] 2018-06-15 09:40:13.539 ERROR 9168 -- [niO-7000-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service()for servlet [dispatcherServlet] incontext with path [/demo] threw exception [Request processing failed;  nested exception is java.lang.ArithmeticException: / by zero] with root cause java.lang.ArithmeticException: / by zero at com.example.demo.controller.GetUserInfoController.lambdaA $5(GetUserInfoController.java:93) ~[classes/:na]
	at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$4(WebAsyncManager. Java: 317) ~ [spring - web - 5.0.6. The jar: 5.0.6. RELEASE] at Java. Util. Concurrent. The Executors$RunnableAdapterCall (Executors. Java: 511) ~ [na: 1.8.0 comes with _161] at Java. Util. Concurrent. FutureTask. Run (FutureTask. Java: 266) ~ [na: 1.8.0 comes with _161] the at Java. Lang. Thread. The run (Thread. Java: 748) [na: 1.8.0 comes with _161] = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = HTTP - nio - 7000 - exec - 2 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = task 3 error occurred! Task 3 is complete!Copy the code

Of course, you can also do some exception handling on this so that it doesn’t look unfriendly to users. For exception handling, see my other post on the use of Spring Boot /Spring Unified error handling

Browser output:

3. Timeout asynchronous tasks

Interface call: http://localhost:7000/demo/getUserWithTimeOut.json

/** * Tests asynchronous tasks with task timeout * @return
 */
@RequestMapping(value = "getUserWithTimeOut.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithTimeOut() {
    System.err.println("The main Thread name is "+ Thread.currentThread().getName()); WebAsyncTask<String> Task2 = new WebAsyncTask<String>(10 * 1000L, () -> {system.err.println ()"The second Thread name is " + Thread.currentThread().getName());
    	Thread.sleep(20 * 1000L);
    	return "Task 2 timed out!"; }); Task2.ontimeout (() -> {system.err.println ()"= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =" + Thread.currentThread().getName()
    			+ "= = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
    	return "Task 2 timed out!"; }); Task2. onCompletion(() -> {system.err.println ()"Mission 2 completed!");
    });
    
    System.err.println("Task2 moves on to other things!");
    return task2;
}
Copy the code

Console execution result:

The main Thread name is http-nio-7000-exec-4 task2. The second Thread name is MvcAsync2 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = HTTP - nio - 7000 - exec - 5 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2 perform task!Copy the code

Browser execution result:

Thread pool asynchronous tasks

Asynchronous tasks by default in the above three conditions is not the thread pool mechanism is adopted to improve the management, that is, a request to come in, although the release of the processing threads, but the system still thread for each request to create an asynchronous task, namely the MvcAsync we see above the beginning of the asynchronous task thread, so that I can’t, that overhead is particularly big ah! So we can manage thread pools by passing in an instance of the ThreadPoolTaskExecutor object directly to the WebAsyncTask class constructor.

Below we look at first, when the first case above the execution of concurrent requests what happens (here to simulate concurrent invocations on http://localhost:7000/demo/getUserWithNoThing.json) :

The console output is as follows:

The first Thread name is MvcAsync57
The first Thread name is MvcAsync58
The first Thread name is MvcAsync59
The first Thread name is MvcAsync60
The first Thread name is MvcAsync61
The first Thread name is MvcAsync62
The first Thread name is MvcAsync63
The first Thread name is MvcAsync64
The first Thread name is MvcAsync65
The first Thread name is MvcAsync66
The first Thread name is MvcAsync67
The first Thread name is MvcAsync68
The first Thread name is MvcAsync69
The first Thread name is MvcAsync70
The first Thread name is MvcAsync71
The first Thread name is MvcAsync72
The first Thread name is MvcAsync73
The first Thread name is MvcAsync74
The first Thread name is MvcAsync76
The first Thread name is MvcAsync75
The first Thread name is MvcAsync77
The first Thread name is MvcAsync78
The first Thread name is MvcAsync79
The first Thread name is MvcAsync80
Copy the code

Since there is no thread pool, 100 requests will start 100 asynchronous task threads, which is extremely expensive and not recommended.

Here is an implementation using thread pools:

Call interface: http://localhost:7000/demo/getUserWithExecutor.json

/** * test thread pool * @return
 */
@RequestMapping(value = "getUserWithExecutor.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithExecutor() {
    System.err.println("The main Thread name is "+ Thread.currentThread().getName()); WebAsyncTask<String> Task1 = new WebAsyncTask<String>(10 * 1000L, executor, () -> { System.err.println("The first Thread name is " + Thread.currentThread().getName());
    	Thread.sleep(5000L);
    	return "Mission 4 was successfully executed! No exceptions are thrown!"; }); Task1.oncompletion (() -> {system.err.println ()"Mission 4 is complete!");
    });
    
    System.err.println("Task4 moves on to other things!");
    return task1;
}
Copy the code

Thread pools are defined as follows:

@Configuration
public class MyExecutor {
    
    @Bean
    public static ThreadPoolTaskExecutor getExecutor() {
    	ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    	taskExecutor.setCorePoolSize(30);
    	taskExecutor.setMaxPoolSize(30);
    	taskExecutor.setQueueCapacity(50);
    	taskExecutor.setThreadNamePrefix("huang"); // Asynchronous task thread names are prefixed with huangreturntaskExecutor; }}Copy the code

A concurrent test of the above yields the following results:

This article sample code address: github.com/SmallerCode…

Using thread pool can save server resources, optimize server processing capacity, to remember common yo! Thanks for reading! Feel helpful to you, please give a start oh!