이전 글에서 Async로 정의된 메소드를 실행하는 걸 JUnit으로 테스트 하던 중 비정상적으로 테스트가 빨리 끝나는 것을 확인 할 수 있었다.
이는 멀티쓰레드로 돌고 있는 비동기 메소드들이 프로세스 종료에 따라 Thread interrupt가 걸려서 생기는 문제였었다.
이번 글에서는 해당 문제를 해결하는 과정을 서술해보고자 한다.
문제 해결에 핵심 비동기로 선언된 메소드들이 끝나는 것을 알 수 있어야한다는 것이였다. 따라서 이를 위해 비동기 함수의 반환값을 Future로 처리하였다.
Future
Future는 Java에서 비동기로 처리된 메소드들에 대하여 미래에 받아올 수 있는 방식을 정의하는 사용된다.
모르겠는거에 최고는 공식 문서나 소스코드를 읽는거라 생각이 든다. 이번에는 소스코드를 까보도록 하겠다.
public interface Future<V> {
/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason.
...
*/
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
소스 코드에서 알 수 있듯이 Future는 비동기 메소드의 처리 결과에 대한 응답만을 받을 수 있는 클래스이다. 직접적으로 비동기 메소드를 끝내는 메소드는 존재하지 않는데 이러한 결과를 위해서는 CompletableFuture를 사용해야한다.
나는 여기서 get 함수를 사용해서 문제를 해결했다.
get은 비동기 메소드의 결과값을 반환하는데 사용하는 메소드로 이를 사용하면 해당 메소드가 결과를 반환하기 전까지 메인쓰레드가 멈추게 된다.
이를 이용하기 위해 코드를 다음과 같이 수정하였다
먼저 service 코드이다.
@Slf4j
@Service
public class TestService {
@Async
public Future<Boolean> asyncMethod(int threadNumber){
try{
for(int i = 0 ; i < 100 ; i ++){
log.error(threadNumber + "th thread count :" + i);
Thread.sleep(100);
}
log.info("Thread " + threadNumber + " Complete : " + LocalDateTime.now());
return new AsyncResult<>(true);
}catch(InterruptedException e){
log.info(e.getMessage());
return new AsyncResult<>(false);
}
}
}
다음은 controller 코드 부분이다.
@Slf4j
@Controller
public class AdminController {
@Autowired
TestService testService;
@PostMapping("admin/test")
public ResponseEntity<?> testController() throws ExecutionException, InterruptedException {
List<Future<Boolean>> asyncResultWaitingQueue = new ArrayList<>();
for(int i = 0 ; i < 5 ; i++){
asyncResultWaitingQueue.add(testService.asyncMethod(i));
}
for(Future<Boolean> result : asyncResultWaitingQueue){
result.get();
}
log.info("Main Process Done : " + LocalDateTime.now());
return ResponseEntity.ok().body(LocalDateTime.now());
}
}
보시다 싶이 Service 메소드의 반환 타입은 Future<>로 선언을 했고 Spring에서는 이에 대한 반응을 올바르게 출력시키기위해
return new AsyncResult<>(반환값)
을 정의했다.
이후 controller는 이들을 위한 list를 만들어 해당 list를 순회하면서 모든 쓰레드가 정상적으로 끝나는지 검사하였다.
긴 글 읽어주셔서 감사합니다.
틀린 부분이 있으면 댓글을 달아주시면 감사하겠습니다.
📧 : may3210@g.skku.edu
'개발 > Spring' 카테고리의 다른 글
[Spring] SpringSecurity와 Jwt : 이론편 (0) | 2023.01.06 |
---|---|
[Spring ] SLF4J을 이용한 스프링 로그 작성 (0) | 2022.02.15 |
[Spring]JUnit에서 Async메소드의 비정상적인 종료 - 1편 (0) | 2022.02.10 |
[Spring] Rest Docs 빌드 부터 사용까지 (2) | 2022.01.24 |
[Spring] Spring과 Paging - [1] (0) | 2022.01.20 |