Use steps
- Custom application YML configuration items and their values
- Create a thread pool configuration class
- Create two interfaces OrderService and AsyncOrderService and their implementation classes
- Create Controller for testing
- Observe the order and effect of the printed content on the console
5.1 Use @ Async
5.5 Don't use @ Async
Case requirements: after the user places an order, the server generates an order and saves it, and then sends a text message and e-mail to the user to notify the success of the order
Because sending SMS and e-mail is time-consuming and second level, asynchronous mode should be adopted. There are many implementation schemes. Here, the ThreadPoolTaskExecutor, a thread pool encapsulated by ThreadPoolExecutor provided by Spring, can be used directly on the business method with the annotation @ Async (start with @ EnableAsync first)
The execution rules of thread pool ThreadPoolExecutor are as follows
1. Customize the application YML configuration items and their values
It corresponds to the parameter of creating thread pool. See the member attribute note in step 2 for the specific meaning. The parameter can be modified according to the actual situation. It is only used for demonstration here
async: # Asynchronous order task thread pool parameters order-service: core-pool-size: 20 max-pool-size: 100 keep-alive-seconds: 10 queue-capacity: 200 thread-name-prefix: async-order-service-
2. Write thread pool configuration class
@Setter @ConfigurationProperties(prefix = "async.order-service") @EnableAsync @Configuration public class AsyncOrderServiceConfig { /** * Number of core threads (default threads) */ private int corePoolSize; /** * Maximum number of threads */ private int maxPoolSize; /** * Allowed thread idle time (unit: default is seconds) */ private int keepAliveSeconds; /** * Buffer queue size */ private int queueCapacity; /** * Thread pool name prefix */ private String threadNamePrefix; @Bean public ThreadPoolTaskExecutor asyncOrderService() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize(corePoolSize); threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize); threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds); threadPoolTaskExecutor.setQueueCapacity(queueCapacity); threadPoolTaskExecutor.setThreadNamePrefix(threadNamePrefix); // Processing strategy of thread pool for rejecting tasks threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // When the task is completed, it will be closed automatically. The default value is false threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); // The core thread timed out and exited. The default value is false threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true); threadPoolTaskExecutor.initialize(); return threadPoolTaskExecutor; } }
First, support asynchronous tasks through @ EnableAsync, and then use @ ConfigurationProperties to automatically encapsulate similar configuration information into an entity class. Its attribute prefix represents application The prefix of the configuration item in the YML configuration file, combined with Lombok's @ Setter, ensures that the Value of the configuration file can be injected into the corresponding attribute of this class. Of course, you can use @ Value without @ Setter, but it is cumbersome here (you need to add @ Value(${xxx}) to each attribute)
This step can be omitted because when @ Async is used without specifying the value attribute, SpringBoot will create a SimpleAsyncTaskExecutor thread pool by default to handle asynchronous method calls
The annotation @ Async is defined as follows, with only one value attribute (SpringBoot 2.3.7 RELEASE)
3. Create two interfaces OrderService and AsyncOrderService and their implementation classes
Create order business interface
public interface OrderService { /** * Place an order */ void makeOrder(); /** * Generate order and save to database */ void saveOrder(); }
Create asynchronous order business interface
public interface AsyncOrderService { /** * SMS service */ void sendSms(); /** * mail serve */ void sendEmail(); }
Create implementation classes and write simple logic instead of business logic
@Slf4j @Service public class OrderServiceImpl implements OrderService { @Autowired private AsyncOrderService asyncOrderService; @Override public void makeOrder() { // send message asyncOrderService.sendSms(); // send emails asyncOrderService.sendEmail(); // Save order saveOrder(); } @Override public void saveOrder() { log.info("Generate order and save..."); } }
@Slf4j @Service public class AsyncOrderServiceImpl implements AsyncOrderService { @Async("asyncOrderService") @Override public void sendSms() { try { long start = System.currentTimeMillis(); log.info("Start sending text messages"); // The simulation takes 3s TimeUnit.SECONDS.sleep(3); log.info("SMS sent!"); log.info("Sending SMS takes:{} ms", System.currentTimeMillis() - start); } catch (InterruptedException e) { e.printStackTrace(); } } @Async("asyncOrderService") @Override public void sendEmail() { try { long start = System.currentTimeMillis(); log.info("Start sending mail"); // The simulation takes 2s TimeUnit.SECONDS.sleep(2); log.info("Mail sent!"); log.info("Time taken to send mail:{} ms", System.currentTimeMillis() - start); } catch (InterruptedException e) { e.printStackTrace(); } } }
The @ Async annotation is used here. It only has a value attribute of String type, which is used to specify the Name of a Bean. The type is Executor or TaskExecutor, which means that the specified thread pool is used to execute asynchronous tasks. Here, it is specified to configure the class asyncOrderService for the thread pool in step 2
There are several points to note, which will invalidate @ Async
● if the SpringBoot framework is used, the @ EnableAsync annotation must be added to the startup class
● asynchronous methods cannot be in the same class as the called asynchronous methods
● asynchronous classes do not use @ Component annotation (or other similar annotations), which makes spring unable to scan asynchronous classes
● annotations such as @ Autowired or @ Resource need to be automatically injected into the class, and new objects cannot be manually injected
● asynchronous methods use non public or static modifiers
4. Create Controller for testing
@RestController public class TestController { @Autowired private OrderService orderService; @PostMapping("/makeOrder") public String makeOrder() { orderService.makeOrder(); return "ok"; } }
5. Observe the order in which the console prints logs when @ Async is used and not used
Calling an interface using a modal tool http://localhost:8080/makeOrder , console contents are as follows
Use @ Async
Don't use @ Async
If you need to compare the viewing effect of dynamic graph, it is recommended to view it immediately after refreshing the page, and the effect is the best
Running effects with @ Async
Run effect without @ Async
From the perspective of code logic, the former is asynchronous execution and the latter is synchronous execution;
In terms of execution effect, the completion speed of @ Async is significantly improved