@Usage and examples of Async

Posted by kenyabob on Tue, 08 Mar 2022 13:00:29 +0100

@Usage and examples of Async annotations

background

In general, the method calls in Java are synchronous calls. For example, the B method is invoked in the A method. After the B method is invoked by A, the B method must wait for execution and return, and the A method can continue to execute. A problem that is easy to occur is that if method B takes a long time to execute, it may lead to the slow response of the request calling A. in order to solve this problem, you can use Spirng's annotation * * @ Async to handle it in the way of asynchronous call. Of course, there are other multithreading methods to solve this kind of problem, This article mainly analyzes the usage and specific examples of @ Async * * in solving such problems.

Asynchronous call

For example, instead of calling method B asynchronously, method A will continue to execute method B after method B is called.

@Introduction to Async

In Spring, marking a method with @ Async can make the method asynchronous. When these methods are called, they will be executed in an independent thread, and the caller does not need to wait for the method to complete.

Enable @ Async in Spring

Use * * @ EnableAsync**

@Slf4j
@SpringBootApplication
@ComponentScan(basePackages = {"com.kaesar.spring"})
@EnableAsync // Turn on asynchronous call
public class Application {
    public static void main(String[] args) {
        log.info("spring boot Start...");
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
        String[] activeProfiles = ctx.getEnvironment().getActiveProfiles();
        for (String profile : activeProfiles) {
            log.info("The current environment is:" + profile);
        }
        log.info("spring boot Start successful...");
    }
}

Example 1: basic usage

Add * * @ Async * * annotation on the method

/**
 * Asynchronous method
 * By default, Spring uses SimpleAsyncTaskExecutor to execute these asynchronous methods (this executor does not limit the number of threads).
 * This default value can be overridden from two levels:
 * Method level
 * Application level
 */
@Async
public void test2() {
    try {
        log.info(Thread.currentThread().getName() + " in test2, before sleep.");
        Thread.sleep(2000);
        log.info(Thread.currentThread().getName() + " in test2, after sleep.");
    } catch (InterruptedException e) {
        log.error("sleep error.");
    }
}

Call asynchronous method

/**
 * Call asynchronous methods of different classes
 */
public void func1() {
    log.info("before call async function.");
    asyncService.test2();
    log.info("after call async function.");
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        log.error("sleep error.");
    }
    log.info("func end.");
}

results of enforcement

It can be seen from the execution results that after calling the asynchronous method test2, the func1 method in the main thread does not wait for the execution of test2 method, but directly executes the following code.

Example two: calling asynchronous methods in the same class

The method func2 is in the same class as the asynchronous method test2 above

It can be seen from the execution results that after calling the asynchronous method test2 method, the func2 method in the main thread will not continue to execute until the test2 method is executed.

Example 3: asynchronous method is static method

The asynchronous method test3 is a static method

/**
 * Asynchronous methods cannot be static methods, or the annotation will be invalid
 */
@Async
public static void test3() {
  try {
    log.info(Thread.currentThread().getName() + " in test3, before sleep.");
    Thread.sleep(2000);
    log.info(Thread.currentThread().getName() + " in test3, after sleep.");
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }

}

Call the method of test3

/**
 * Call asynchronous methods of different classes. The asynchronous method is static method
 */
public void func3() {
  log.info(Thread.currentThread().getName() + ": before call async function.");
  AsyncService.test3();
  log.info(Thread.currentThread().getName() + ": after call async function.");
  try {
    Thread.sleep(3000);
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }
  log.info(Thread.currentThread().getName() + ": func end.");
}

Execution results. It can be seen that the * * @ Async * * annotation is added to the static method. When calling this method, a new thread is not enabled to execute alone, but execute the code in sequence, indicating that asynchrony is invalid.

Example 4: modify the default actuator at the method level

Customize a thread pool executor to replace the default executor

Custom thread pool executor

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * Custom thread pool
 */
@Configuration
public class AsyncConfig {
    private static final int MAX_POOL_SIZE = 10;
    private static final int CORE_POOL_SIZE = 5;

    @Bean("asyncTaskExecutor")
    public AsyncTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
        asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
        asyncTaskExecutor.initialize();
        return asyncTaskExecutor;
    }
}

Use custom executors on asynchronous methods

/**
 * Modify the default actuator at the method level
 */
@Async("asyncTaskExecutor")
public void test4() {
  try {
    log.info(Thread.currentThread().getName() + ": in test4, before sleep.");
    Thread.sleep(2000);
    log.info(Thread.currentThread().getName() + ": in test4, after sleep.");
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }
}

Call test4 asynchronous method

/**
 * Call asynchronous methods of different classes
 */
public void func4() {
  log.info(Thread.currentThread().getName() + ": before call async function.");
  asyncService.test4();
  log.info(Thread.currentThread().getName() + ": after call async function.");
  try {
    Thread.sleep(3000);
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }
  log.info(Thread.currentThread().getName() + ": func end.");
}

It can be seen from the execution result that the @ Async annotation declares that the specified custom asynchronous actuator has been replaced by the default actuator. Moreover, the main thread calling the asynchronous method does not wait for the execution of the asynchronous method.

Note: after creating a custom actuator, the annotation @ Async will be replaced with a custom actuator by default, so you don't need to specify it on the * * @ Async * * annotation.

1.0 1 365 ≈ 37.7834343329 1.01^{365} ≈ 37.7834343329 1.01365≈37.7834343329
0.9 9 365 ≈ 0.02551796445 0.99^{365} ≈ 0.02551796445 0.99365≈0.02551796445
Believe in the power of persistence!

Topics: Java Spring Spring Boot