본문 바로가기

Spring

@Async 사용법

@Async 사용법

많은 블로그에도 정리가 잘 나와있는 방법이다.

1. @EnableAsync로 @Async를 쓰겠다고 스프링에게 알린다.

2. 비동기로 수행되었으면 하는 메서드위에 @Async를 적용한다.

스프링 가이드에도 마찬가지로 설명해준다.

만약에 별도로 @Async에 대한 설정이 없으면 새로운 비동기 작업을 스레드 풀에서 처리하는 게 아니라 새로운 스레드를 매번 생성해서 작업을 수행시키는 것이 디폴트 설정이다.

그래서 쓰레드풀을 빈으로 등록시켜줘서 자동으로 해당 스레드 풀로 작업을 넘기도록 설정한다.

 

@Configuration 
@EnableAsync 
public class AsyncThreadConfiguration { 
    @Bean public Executor asyncThreadTaskExecutor() { 
    	ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); 
        threadPoolTaskExecutor.setCorePoolSize(8);
        threadPoolTaskExecutor.setMaxPoolSize(8); 
        threadPoolTaskExecutor.setThreadNamePrefix("jeong-pro-pool"); 
        return threadPoolTaskExecutor; 
    } 
}

 

위와 같이 configuration 클래스를 하나 만들고 Bean을 등록하면 자동으로 내가 만든 스레드 풀에 작업이 할당될 것이다.

springboot 2.0 이상이라면 auto configuration으로 Executor를 등록해주기 때문에 아래와 같이 설정 파일에서 설정해도 똑같이 스레드 풀이 생성 및 적용될 것이다. (application.yml)

 

spring: 
    task: 
    	execution: 
             pool: 
                core-size: 8 
                max-size: 8

 

위와 같이 설정단계를 거쳤으면 아래 코드처럼 @Async를 통해 호출할 수 있다.

 

@RestController public class TestController { 
    @Autowired private TestService testService; 

    @GetMapping("/test2") 
    public void test2() { 
    	for(int i=0;i<10000;i++) {
        	testService.innerMethodCall(i); 
        } 
    } 
}

 

@Service public class TestService {
    private static final Logger logger = LoggerFactory.getLogger(TestService.class); 
    
    @Async public void asyncHello(int i) { 
    	logger.info("async i = " + i);
    } 
}

주의사항
@Async는 은 탄환이 아니다.

  • private 메서드에는 적용이 안된다. public만 된다.
  • self-invocation(자가 호출)해서는 안된다. -> 같은 클래스 내부의 메서드를 호출하는 것은 안된다.
  • 리턴값에 대해서 void나 CompletableFuture<>

위와 같은 주의사항이 있다. 

 

@RestController public class TestController { 
    @Autowired private TestService testService; 

    @GetMapping("/test2") 
    public void test2() { 
    	for(int i=0;i<10000;i++) {
        	testService.innerMethodCall(i); 
        } 
    } 
}

 

@Service public class TestService { 
    private static final Logger logger = LoggerFactory.getLogger(TestService.class);
    
    @Async public void innerMethod(int i) { 
    	logger.info("async i = " + i); 
    } 
    
    public void innerMethodCall(int i) { innerMethod(i); } 
}

 

위과 같이 실행하게되면 동기적으로 실행된다.

 

이유

 

위의 출처에서 제대로 설명해준다.

결론부터 말하면 AOP가 적용되어 Spring context에 등록되어 있는 빈 객체의 메서드가 호출되었을 때 스프링이 끼어들 수 있고 @Async가 적용되어 있다면 스프링이 메서드를 가로채서 다른 스레드(풀)에서 실행시켜주는 메커니즘이라는 것이다.

출처 - https://dzone.com/articles/effective-advice-on-spring-async-part-1

그렇기 때문에 위에 제약조건이었던 것들이 이해가 된다.

public이어야 가로챈 스프링의 다른 클래스에서 호출이 가능할 것이고,

self-invocation이 불가능 했던 이유도 spring context에 등록된 빈의 메서드 호출이어야 프록시를 적용받을 수 있기에 내부 메서드 호출은 프록시 영향을 받지 않기 때문이다.

 

 

 

'Spring' 카테고리의 다른 글

MapStruct 사용법  (0) 2021.09.15
MVC패턴과 동작방식  (0) 2020.11.18
Ioc/DI 제어의 역전/의존성 주입  (0) 2020.11.18