@Async 사용법
2023. 7. 31. 21:45ㆍJava/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가 동작하지 않습니다.
참고
반응형
'Java > Spring' 카테고리의 다른 글
Spring Security - Rest API에 csrf protection을 사용하지 않는 이유 (0) | 2022.05.02 |
---|---|
Spring MVC (0) | 2021.11.02 |
Spring MVC와 WebFlux의 차이 (0) | 2021.10.29 |
Spring Batch Chunk 지향 처리 (0) | 2021.03.22 |
Spring + Hibernate Validator Custom Message (0) | 2020.09.15 |