Asynchronous invocation corresponds to synchronous invocation, which can be understood as sequential execution in defined order. Asynchronous calls can continue execution without waiting for the end of the previous instruction call.

We will demonstrate this in creating a Spring Boot project. For specific projects, please refer to github code github.com/UniqueDong/… Async module

Pom dependencies are as follows:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <! -- logback -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
Copy the code

The startup classes are as follows:

@SpringBootApplication
public class AsyncApplication {

    public static void main(String[] args) { SpringApplication.run(AsyncApplication.class, args); }}Copy the code

Defining a thread pool

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/** * asynchronous thread pool */
@Configuration
@EnableAsync
public class AsyncExecutorConfig {

    /** * Set the ThreadPoolExecutor's core pool size. */
    private int corePoolSize = 8;
    /** * Set the ThreadPoolExecutor's maximum pool size. */
    private int maxPoolSize = 16;
    /** * Set the capacity for the ThreadPoolExecutor's BlockingQueue. */
    private int queueCapacity = 200;

    private String threadNamePrefix = "AsyncExecutor-";

    @Bean("taskExecutor")
    public Executor taskExecutor(a) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix(threadNamePrefix);

        Rejection -policy: how to deal with a new task when the pool reaches Max size
        // CALLER_RUNS: the task is not executed in a new thread, but in the caller's thread
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        returnexecutor; }}Copy the code

In this code we create a thread pool using ThreadPoolTaskExecutor. Parameter meanings are as follows:

  • CorePoolSize: The number of core threads created by the thread pool
  • MaxPoolSize: The maximum number of threads in the thread pool that will be subscribed to when the number of tasks exceeds corePoolSize and the buffer queue is full.
  • SetKeepAliveSeconds: Allow threads to be idle for 60 seconds, and destroy the maxPoolSize thread when the idle time arrives.
  • ThreadNamePrefix: The prefix task name of the thread.
  • RejectedExecutionHandler: If the thread pool is not capable of processing, this policy will run the rejected task directly in the execute thread. If the executor is closed, the task is discarded

Use of actual combat

@Slf4j
@Service
public class OrderService {
    public static Random random = new Random();


    @Autowired
    private AsyncTask asyncTask;

    public void doShop(a) {
        try {
            createOrder();
            // Invoke the asynchronous task with the result returned
            Future<String> pay = asyncTask.pay();
            if (pay.isDone()) {
                try {
                    String result = pay.get();
                    log.info("Asynchronous task return result {}", result);
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                asyncTask.vip();
                asyncTask.sendSms();
            }
            otherJob();
        } catch (InterruptedException e) {
            log.error("Abnormal", e); }}public void createOrder(a) {
        log.info("Start task 1: Order successful");
    }

    /** * the caller cannot be in the same class as the caller. The main use of dynamic proxy, the same class when the direct call, not through the generated dynamic proxy class call */
    @Async("taskExecutor")
    public void otherJob(a) {
        log.info("Get started on Task 4: Logistics");
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(random.nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        log.info(Complete Task 4, Time: + (end - start) + "毫秒"); }}Copy the code

Asynchronous task service class

mport lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.Future;

@Component
@Slf4j
public class AsyncTask {
    public static Random random = new Random();


    @Async("taskExecutor")
    public void sendSms(a) throws InterruptedException {
        log.info("Start task 2: Send a text message.");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info(Complete Task 1, Time: + (end - start) + "毫秒");
    }

    @Async("taskExecutor")
    public Future<String> pay(a) throws InterruptedException {
        log.info("Start doing asynchronous return results Task 2: Pay");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info(Complete Task 2, Time: + (end - start) + "毫秒");
        return new AsyncResult<>("Membership Service completed");
    }

    /** * return the result of the asynchronous call *@throws InterruptedException
     */
    @Async("taskExecutor")
    public void vip(a) throws InterruptedException {
        log.info("Get started on Task 5: Membership");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("Start asynchronous return result task 5, time:" + (end - start) + "毫秒"); }}Copy the code

Unit testing

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AsyncApplication.class)
public class AsyncApplicationTests {

    @Autowired
    private OrderService orderService;

    @Test
    public void testAsync(a) {
        orderService.doShop();
        try {
            Thread.currentThread().join();
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

The results show

2019- 05-16 20: 25: 06.577 [INFO ] [main] - zero.springboot.study.async.service.OrderService-52Start task 1: Place an order successfully 2019- 05-16 20: 25: 06.586 [INFO ] [main] - zero.springboot.study.async.service.OrderService-60Start task 4: Logistics 2019- 05-16 20: 25: 06.599 [INFO ] [AsyncExecutor-1] - zero.springboot.study.async.service.AsyncTask-38Start doing asynchronous return results task 2: Pay 2019- 05-16 20: 25: 13.382 [INFO ] [AsyncExecutor-1] - zero.springboot.study.async.service.AsyncTask-42Complete Task 2, time: 6783 ms 2019- 05-16 20: 25: 14.771 [INFO ] [main] - zero.springboot.study.async.service.OrderService-68Complete Task 4, 8184 millisecondsCopy the code

You can see that some thread names are prefixes defined by our thread pool, indicating that thread pools are used for asynchronous execution. We demonstrate an incorrect use case, otherJob(), which is not executed asynchronously.

The reason:

Spring scans the bean for the @Async annotation on the method. If it does, Spring dynamically generates a subclass (proxy) for the bean. The proxy class inherits from the original bean. At this point, when the annotated method is called, it is actually called by the proxy class, which adds an asynchronous role to the invocation. However, if the annotated method is called by another method in the same class, then the method is not called through the proxy class, but directly through the original bean, this. method, so there is no added Async effect, and we see @async annotation invalid.

Follow the public account JavaStorm