Today we will learn how to perform asynchronous programming in Spring. We all know that the threads that the web server processes request request are obtained from the thread pool, which is not difficult to explain, because when the number of web requests is very large, how to create a processing thread when a request comes in. Since the overhead of creating threads and thread context switching is relatively large, the web server will eventually face a crash. In addition, the processing threads created by the web server are executed synchronously from beginning to end by default. That is to say, if processing thread A is responsible for processing request B, then when B does not return , processing thread A cannot escape to process other requests, which will greatly limit the concurrent processing capability of the web server.
Therefore, the thread pool solves the problem of thread recycling, so how to solve the synchronous processing request? The answer is asynchronous processing. What is asynchronous processing? Asynchronous processing mainly allows the above B request to be idle before the above request processing is completed, and thread A can be freed to continue processing other requests. Then we can do this, restart thread C within thread A to execute the task, let A return directly to the web server, and continue to accept new requests.
Before starting the explanation below, I will first distinguish two concepts here:
1. Process threads
The processing thread belongs to the web server, is responsible for processing user requests, and is managed by thread pool
2. Asynchronous threading
Asynchronous threads are user-defined threads and can be managed by thread pools.
Spring provides support for asynchronous tasks. Asynchronous tasks can be implemented using WebAsyncTask class. At the same time, we can also set corresponding callback processing for asynchronous tasks, such as how to handle when the task timed out, and how to throw an exception. Asynchronous tasks are usually very practical. For example, we want to leave an operation that may be processed for a long time to the asynchronous thread to process, or when an order is paid, we enable the asynchronous task to query the payment result of the order.
1. Normal asynchronous tasks
For demonstration convenience, 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:
/** * Test asynchronous tasks without any exception*/@RequestMapping(value = "getUserWithNoThing.json", method = RequestMethod.GET)public WebAsyncTask<String> getUserWithNoThing() { // Print processing thread name System.err.println("The main Thread name is " + Thread.currentThread().getName()); // This simulates opening an asynchronous task, with a timeout of 10s WebAsyncTask<String> task1 = new WebAsyncTask<String>(10 * 1000L, () -> { System.err.println("The first Thread name is " + Thread.currentThread().getName()); // The task processing time is 5s, and the timeout is not Thread.sleep(5 * 1000L); return "Task 1 executes successfully! No exception is thrown!"; }); // The method is called when the task execution is completed task1.onCompletion(() -> { System.err.println("Task 1 executes completed!"); }); System.err.println("task1 continues to handle other things!"); return task1;}The console prints as follows:
The main Thread name is http-nio-7000-exec-1
task1 keeps dealing with other things!
The first Thread name is MvcAsync1
Task 1 has been completed!
The browser results are as follows:
2. Exceeding exception asynchronous task
Interface call: http://localhost:7000/demo/getUserWithError.json
/** * Test the asynchronous task where an error occurs* @return */@RequestMapping(value = "getUserWithError.json", method = RequestMethod.GET)public WebAsyncTask<String> getUserWithError() {System.err.println("The main Thread name is " + Thread.currentThread().getName());// This simulates opening an asynchronous task. WebAsyncTask<String> task3 = new WebAsyncTask<String>(10 * 1000L, () -> {System.err.println("The second Thread name is " + Thread.currentThread().getName());// Exception thrown here int num = 9 / 0;System.err.println(num);return "";});// Call this method when an exception occurs task3.onError(() -> {System.err.println("================================================================================================================================================================================================================================================================================================================================================================================================================ "==============================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================The console output is as follows:
The main Thread name is http-nio-7000-exec-1
task3 continues to deal with other things!
The second Thread name is MvcAsync1
2018-06-15 09:40:13.538 ERROR 9168 --- [nio-7000-exec-2] oaccC[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] throw exceptionjava.lang.ArithmeticException: / by zero
at com.example.demo.controller.GetUserInfoController.lambda$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.RELEASE.jar:5.0.6.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_161]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_161]
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] oaccC[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/demo] throw 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.lambda$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.RELEASE.jar:5.0.6.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_161]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_161]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]============================================================================================
Mission 3 has occurred!
Task 3 has been completed!
Of course, you can also do some exception handling on the above to avoid unfriendly to the user's opinion. For exception handling, you can check out another article in my article on the use of Spring boot/Spring unified error handling scheme
Browser output results:
3. Timeout asynchronous task
Interface call: http://localhost:7000/demo/getUserWithTimeOut.json
/** * Test the asynchronous task in which the task timed out* @return */@RequestMapping(value = "getUserWithTimeOut.json", method = RequestMethod.GET)public WebAsyncTask<String> getUserWithTimeOut() { System.err.println("The main Thread name is " + Thread.currentThread().getName()); // This is simulated to start an asynchronous task, timeout of 10s 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 execution timeout!"; }); // Task timeout calls this method task2.onTimeout(() -> { System.err.println("=================================================================================================================================================================================================================================================================================================================================================================================================================== "==============================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================Console execution results:
The main Thread name is http-nio-7000-exec-4
task2 continues to deal with other things!
The second Thread name is MvcAsync2
==================================================================================================
Task 2 has been completed!
Browser execution results:
4. Thread pool asynchronous tasks
The asynchronous tasks in the above three cases are not managed by the thread pool mechanism by default. That is to say, if a request comes in, although the processing thread is released, the system will still create an asynchronous task thread for each request, which is the asynchronous task thread starting with MvcAsync as we saw above. That is, this won't work, the overhead is particularly high! Therefore, we can use thread pool for management and directly pass a ThreadPoolTaskExecutor object instance in the WebAsyncTask class constructor.
Let's first look at what happens when performing concurrent requests in the first case above (here we simulate concurrent calls to 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
Since the thread pool is not added, 100 requests will open 100 asynchronous task threads, which is particularly expensive and is not recommended.
The following is the implementation of thread pool:
Calling 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()); // This is simulated to start an asynchronous task, and a thread pool is passed here. WebAsyncTask<String> task1 = new WebAsyncTask<String>(10 * 1000L, executor, () -> { System.err.println("The first Thread name is " + Thread.currentThread().getName()); Thread.sleep(5000L); return "Task 4 executes successfully! No exception was thrown!"; }); // Call this method when the task execution is completed task1.onCompletion(() -> { System.err.println("Task 4 executes completed!"); }); System.err.println("task4 continues to handle other things!"); return task1;}The thread pool is defined as follows:
@Configurationpublic 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 name is huang prefix return taskExecutor; }}Concurrent tests above can be used to obtain the following results:
The sample code address of this article: https://github.com/SmallerCoder/WebAsyncTask
Using thread pools can save server resources and optimize server processing capabilities. Remember to use them frequently! Thanks for reading! If you think it will be helpful to you, please start!
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.