SpringBoot @Async + Completable implements pits stepped on asynchronously

Recently, when using SpringBoot @Async annotation + Java8 Completable to implement asynchrony in the project, I accidentally stepped on the pit, so I wrote this article to summarize.

1. Configuration class

Step 1: Create a new configuration class and implement the AsyncConfigurer interface;
Step 2: Implement getAsyncExecutorthe method, which returns a thread pool object;
Step 3: Implement getAsyncUncaughtExceptionHandlerthe method, which returns a SimpleAsyncUncaughtExceptionHandler object, which is used to simply record exceptions Information;
Step 3: Use the @EnableAsync annotation on the configuration class;

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
    
    

    @Override
    @Bean(name = "taskExecutor")
    public Executor getAsyncExecutor() {
    
    
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(100);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("order-Executor-");
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    
    
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

2. Implement asynchronous methods

The first step: create a new component class, which can be any component managed by Spring, such as service, component, etc.; the
second step: provide an asynchronous method in the class, and use @Async annotation on the method;

@Service
public class DemoService {
    
    

    @Async("taskExecutor")
    public CompletableFuture<String> asyncTask1() {
    
    
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("test1");
    }

    @Async("taskExecutor")
    public CompletableFuture<String> asyncTask2() {
    
    
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("test2");
    }

    @Async("taskExecutor")
    public CompletableFuture<String> asyncTask3() {
    
    
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("test3");
    }
 }

Each of the above asynchronous methods takes 3 seconds to execute.

3. Call the asynchronous method

Here the asynchronous method defined above must be called on another class.

@SpringBootApplication
@Controller
public class AsynctestApplication {
    
    

    @Autowired
    DemoService demoService;

    public static void main(String[] args) {
    
    
        SpringApplication.run(AsynctestApplication.class, args);
    }

    @RequestMapping("/index")
    @ResponseBody
    public String index() {
    
    
        long start = System.currentTimeMillis();
        CompletableFuture<String> task1 = demoService.asyncTask1();
        CompletableFuture<String> task2 = demoService.asyncTask2();
        CompletableFuture<String> task3 = demoService.asyncTask3();
        CompletableFuture[] collect = {
    
    task1, task2, task3};
        CompletableFuture<List<String>> res =  CompletableFuture.allOf(collect).thenApply(ignoredVoid -> {
    
    
            List list = Arrays.stream(collect).map(CompletableFuture::join).collect(Collectors.toList());
            System.out.println(list);
            return list;
        });
        try {
    
    
            // 控制异步任务的执行时间
            res.get(5, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        } catch (TimeoutException e) {
    
    
            
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("一共花费了" + (end - start) + "毫秒");
        return "index";
    }
 }

Judging from the running results, because the three methods are executed asynchronously and simultaneously, the program ends after running for more than 3 seconds.
insert image description here
Finally, let me talk about the pit I stepped on, that is, at the beginning, the method that calls the asynchronous code is also implemented in the class where the asynchronous method is located, resulting in the failure of the execution of the asynchronous method. For example, the following query method:

@Component
public class DemoService {
    
    

    @Async("taskExecutor")
    public CompletableFuture<String> asyncTask1() {
    
    
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("test1");
    }

    @Async("taskExecutor")
    public CompletableFuture<String> asyncTask2() {
    
    
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("test2");
    }

    @Async("taskExecutor")
    public CompletableFuture<String> asyncTask3() {
    
    
        try {
    
    
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("test3");
    }

    public List<String> query() {
    
    
        CompletableFuture<String> task1 = asyncTask1();
        CompletableFuture<String> task2 = asyncTask2();
        CompletableFuture<String> task3 = asyncTask3();
        CompletableFuture[] collect = {
    
    task1, task2, task3};
        CompletableFuture<List<String>> res =  CompletableFuture.allOf(collect).thenApply(ignoredVoid -> {
    
    
            List list = Arrays.stream(collect).map(CompletableFuture::join).collect(Collectors.toList());
            return list;
        });
        try {
    
    
            return res.get(5, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        } catch (TimeoutException e) {
    
    
            e.printStackTrace();
        }
        return null;
    }

}

Because the query method is placed in the same class as other asynchronous methods, the asynchronous function of the program fails. At this time, even if the task times out, no java.util.concurrent.TimeoutExceptionexception will be thrown.
insert image description here

Guess you like

Origin blog.csdn.net/zhongliwen1981/article/details/118355205