@Async 사용법

2023. 7. 31. 21:45Java/Spring

@Async 사용법

@EnableAsync
@SpringBootApplication
public class DemoApplication {

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

}

@Service
public class MessageService {

    @Async
    public void print(String message) {
        System.out.println(message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() {
        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }
}

@EnableAsync 어노테이션을 붙여주고 비동기 방식으로 처리하고 싶은 메소드 위에 @Async 어노테이션을 붙이면 됩니다. 하지만 현재 코드는 스레드를 관리하지 않는다는 문제가 있습니다. 왜냐하면 @Async의 기본 설정은 SimpleAsyncTaskExecutor를 사용하도록 되어 있는데, 이것은 스레드 풀이 아니고 단순히 스레드를 만들어내는 역할을 하기 때문입니다.

@Async 스레드 풀을 사용하는 방법

@Configuration
@EnableAsync // Application이 아닌, Async 설정 클래스에 붙여야 함.
public class SpringAsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3); // 기본 스레드 수
        taskExecutor.setMaxPoolSize(30); // 최대 스레드 수
        taskExecutor.setQueueCapacity(100); // Queue 사이즈
        taskExecutor.setThreadNamePrefix("Executor-");
        return taskExecutor;
    }

}

위와 같이 비동기를 위한 스레드 풀을 설정해줍니다. 스레드 풀을 설정한 이후에는 아래와 같이 코드를 작성해주면 됩니다.

@Service
public class MessageService {

    @Async("threadPoolTaskExecutor")
    public void print(String message) {
        System.out.println(message);
    }
}

리턴별 @Async 사용방법

리턴 값이 없는 경우에는 앞선 코드들과 같이 스레드 풀을 빈으로 생성해 @Async 어노테이션을 붙여서 사용하면 됩니다.

리턴 값이 있는 경우에는 Future, ListenableFuture, CompletableFuture 타입들을 리턴 타입으로 사용할 수 있습니다.
여기서 Future의 경우, 블로킹을 통해 요청 결과가 올 때까지 기다리기 때문에 비동기 블로킹 방식이 되어 성능이 좋지 않습니다.

ListenableFuture

@Service
public class MessageService {

    @Async
    public ListenableFuture<String> print(String message) throws InterruptedException {
        System.out.println("Task Start - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            ListenableFuture<String> listenableFuture = messageService.print(i + "");
            listenableFuture.addCallback(System.out::println, error -> System.out.println(error.getMessage()));
        }
    }
}

ListenableFuture만으로도 논브로킹 코드를 구현할 수 있지만 콜백 안에 콜백이 필요한 경우 매우 복잡한 코드를 유발하게 됩니다.

CompletableFuture

@Service
public class MessageService {

    @Async
    public CompletableFuture<String> print(String message) throws InterruptedException {
        System.out.println("Task Start - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message).completable();
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            CompletableFuture<String> completableFuture = messageService.print(i + "");
            completableFuture
                    .thenAccept(System.out::println)
                    .exceptionally(error -> {
                        System.out.println(error.getMessage());
                        return null;
                    });
        }
    }
}

@Async의 특징

@Async의 장점

  • 어노테이션 하나로 간단하게 동기, 비동기 설정을 할 수 있습니다.

@Async 주의사항

  • @EnableAsync 어노테이션을 선언해야 @Async 기능을 사용할 수 있는데, 별도의 설정이 없다면 프록시 모드로 동작합니다. 이럴 경우에 private 메소드에는 AOP가 동작하지 않습니다. 같은 객체 내의 메소드끼리 호출할 때도 @Async가 동작하지 않습니다.

참고

https://steady-coding.tistory.com/611

728x90