2022-04-15 21:10:20
다소 황당한 블로그 주제지만 실제로 궁금증이 생긴 뒤 이를 해결해나간 과정을 적어보고자 합니다.
회사에서 Spring Batch 코드를 수정할 일이 있어 보던 중, 문득 이런 생각이 들었습니다.
private final BatchService batchService;
public anytype updateSome() {
batchService.task1(); // 5초
batchService.task2(); // 10초
batchService.task3(); // 15초
...
}
위의 상황에서 updateSome 메소드가 종료되려면 총 30초가 소요되는데, 이를 병렬로 해결하면 가장 오래걸리는 task의 시간인 15초로 줄일 수 있지 않을까???
각각의 메소드들은 서로에게 영향을 미치지 않는 독립적인 메소드들이어서, 병렬로 실행해도 괜찮겠다는 생각이 들었습니다. (각각 배치의 트랜잭션에 관한 고민은 하지 않고, 메소드들을 어떻게 병렬로 돌릴 수 있을지에 대해서만 고민하겠습니다.)
해 본 적은 없지만, 병렬처리를 위한 parallelStream을 이용하고, 해당 메소드들을 Funcitonal interface로 만들어서 parallelStream에 담은 후 적절한 쓰레드 수를 할당하여 실행하면 될 것 같은 느낌이 들었습니다.
아이디어는 괜찮다는 생각이 들었지만, 이상하게 구글 검색에서는 답을 쉽게 알려주지 않았습니다. 아마 이러한 상황이 여러 위험요소도 있고, 통상적인 케이스(?)가 아니기 때문일 것입니다.
parallelStream에 담기 적합한 Functional Interface를 찾기
Runnable
을 적용하기로 합니다.
ForkJoinPool을 이용하여 안전한 ParallelStream구현을 위한 적절한 쓰레드 수 구현 및 반납
JDK11버전 이후부터 ForkJoinPool의 shutdown을 반드시 호출해주어야 함.
private final BatchService batchService;
public void updateSome() {
List<Runnable> runnableList = List.of(
batchService::task1 //5초
, batchService::task2 //10초
, batchService::task3 //15초
);
ForkJoinPool forkjoinPool = new ForkJoinPool(runnableList.size());
forkjoinPool.submit(() -> runnableList.parallelStream().forEach(Runnable::run)).get();
forkjoinPool.shutdown(); // after jdk 11
}
참고 :
내 머리