Spring: @Async annotation and use of AsyncResult and CompletableFuture

@Async Overview

In Spring用@Async注解标记的方法,称为异步方法, it will be executed in an independent thread outside the current thread of the caller,

  • In fact, it is equivalent to ourselves new Thread(()-> System.out.println("hello world !")) executing the corresponding business logic in another thread

异步方法实际的执行交给了 Spring 的 TaskExecutor 来完成。

@Async use

  1. @Async annotation If is used on a class method, it means that the method is an asynchronous method
  2. @Async annotation If is used on a class, then all methods of this class will be executed asynchronously
  3. The class object of the @Async annotation method should be a bean object managed by the Spring container
  4. 调用异步方法类上需要配置上注解@EnableAsync,或者是在启动类上加上@EnableAsync

@Async note

  1. By default (@EnableAsync annotatedmode=AdviceMode.PROXY),同一个类内部没有使用@Async注解修饰的方法调用@Async注解修饰的方法,是不会异步执行的
  2. If you want实现类内部自调用也可以异步, you need to switch the mode= of the @EnableAsync annotationAdviceMode.ASPECTJ
  3. Any parameter type is supported, but the method return value must be void or Future type

@Async code example

Use 1 code implementation: Configure @EnableAsync on the class to call the asynchronous method

import org.springframework.scheduling.annotation.Async;
 
public interface TestService {
    
    
    @Async
    void test();
}
import com.example.service.TestService;
import org.springframework.stereotype.Service;
 
@Service
public class TestServiceImpl implements TestService {
    
    
    @Override
    public void test() {
    
    
        //...
    }
}

Controller class that calls asynchronous methods

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.bind.annotation.*;
 
 
@RestController
@RequestMapping(value = "/test")
@EnableAsync
public class TestContoller {
    
    
 
    @Autowired
    private TestService testService;
 
    @GetMapping(value = "/testAsync")
    public void print() {
    
    
       testService.test();
    }
}

If we remove @EnableAsync on the TestController or create a new TestService object (the object is not loaded into the Spring container), then the print() method in the TestController will be executed synchronously, and the background printing log can also see that only one thread is executing.

Implemented using 2 codes: configure @EnableAsync on the startup class

If @EnableAsync is configured on the startup class, then in the above example code, there is no need to add the @EnableAsync annotation on the Controller

入口类增加了 @EnableAsync 注解,主要是为了扫描范围包下的所有 @Async 注解。

Note 1 code implementation: @Async does not take effect

@Async modifies the test() method, but the test2() method does not modify it

public interface TestService {
    
    
    @Async
    void test();
 
    void test2();
}
public class TestServiceImpl implements TestService{
    
    
    @Override
    public void test() {
    
    
        //...
    }
 
    @Override
    public void test2() {
    
    
        test();//自调用test()方法
    }
}

In this case:The test() method called by the test2() method is not executed asynchronously, there is only one thread

AsyncResult

Spring中提供了一个 Future 接口的子类:AsyncResult,所以我们可以返回 AsyncResult 类型的值。

AsyncResult is an asynchronous method. Asynchronous is mainly used for calling code that needs to run for a long time to return results without blocking the caller.

public class AsyncResult<V> implements ListenableFuture<V> {
    
    
    private final V value;
    private final ExecutionException executionException;
    //...
}

AsyncResult implements the ListenableFuture interface. This object has two properties: return value and exception information.

public interface ListenableFuture<T> extends Future<T> {
    
    
    void addCallback(ListenableFutureCallback<? super T> var1);
    void addCallback(SuccessCallback<? super T> var1, FailureCallback var2);
}

AsyncResult code example

Get the implementation of the asynchronous method return value: return String

public Future<String> test() throws Exception {
    
    
        log.info("开始做任务");
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        log.info("完成任务,耗时:" + (end - start) + "毫秒");
        return new AsyncResult<>("任务完成,耗时" + (end - start) + "毫秒");
}

If a custom type is returned, the String in the Future above is changed to the corresponding entity class.

CompletableFuture

To count how much time it takes to execute the three tasks concurrently, you need to wait until the above three functions have been mobilized, record the time, and calculate the results.

You can also use CompletableFuture to return the results of asynchronous calls

  1. Use CompletableFuture.allOf(task1, task2, task3).join() to achieve the blocking effect before the three asynchronous tasks end.
  2. After all three tasks are completed, calculate the total time taken for the concurrent execution of the three tasks based on the end time - start time.

CompletableFuture code example

@Async
public CompletableFuture<String> doTaskOne() throws Exception {
    
    
    log.info("开始做任务一");
    long start = System.currentTimeMillis();
    Thread.sleep(random.nextInt(10000));
    long end = System.currentTimeMillis();
    log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    return CompletableFuture.completedFuture("任务一完成");
}
@Test
public void test() throws Exception {
    
    
    long start = System.currentTimeMillis();
    CompletableFuture<String> task1 = asyncTasks.test();
    CompletableFuture<String> task2 = asyncTasks.test();
    CompletableFuture<String> task3 = asyncTasks.test();
    CompletableFuture.allOf(task1, task2, task3).join();
    long end = System.currentTimeMillis();
    log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");
}

Thread pool configuration

Google or Baidu, spring thread pool configuration, basically very detailed

Guess you like

Origin blog.csdn.net/yyuggjggg/article/details/129189216