체이닝 함수: CompletableFuture
체이닝 함수는 한 함수의 결과를 바로 다른 함수로 연결하여 호출하는데 사용된다.
즉, 함수가 실행 완료됐을 때 실행할 콜백을 등록하는 것이다.
아래는 Future
의 스레드 블로킹 극복을 위해 만들어진 CompletableFuture
가 체이닝 함수를 통해 어떻게 스레드 블로킹을 방지하는지에 대한 코드이다.
package chap01
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
fun main() {
val startTime: Long = System.currentTimeMillis()
val executor: ExecutorService = Executors.newFixedThreadPool(2)
// CompletableFuture 생성 및 비동기 작업 실행
val completableFuture = CompletableFuture.supplyAsync({
Thread.sleep(1000) // 1초간 대기
return@supplyAsync "결과 반환~"
}, executor)
// 비동기 작업 완료 후 결과 처리를 위해 체이닝 함수 등록
completableFuture.thenAccept { result ->
println("[${getElapsedTime(startTime)}] $result 처리~") // 결과 처리 출력
}
// 비동기 작업 실행 도중 다른 작업 실행
println("[${getElapsedTime(startTime)}] 다른 작업 실행~")
}
fun getElapsedTime(startTime: Long): String =
"지난 시간: ${System.currentTimeMillis() - startTime}ms"
CompletableFuture
객체를 반환받기 위해 작업을 제출할 때는 ExecutorService.submit()
이 아닌 CompletableFuture.supplyAsync()
를 사용해야 한다.
CompletableFuture.supplyAsync()
의 첫 번째 매개변수에는 실행할 코드를 람다식으로 전달하고, 두 번째 매개변수에는 해당 작업이 실행될 ExecutorService
객체를 지정한다.
그러면 CompletableFuture
객체가 반환되는데 이 CompletableFuture
객체에 결과가 반환되었을 때 실행할 콜백을 thenAccept()
를 통해서 등록할 수 있다.
위 코드에서는 결과가 반환되었을 때 “결과 반환~ 처리~” 문자를 출력하도록 하였다.
Future.get()
은 스레드를 블로킹하고 결과를 기다리지만, CompletableFuture.thenAccept()
는 스레드를 블로킹하지 않고 CompletableFuture
객체에 콜백을 등록하기 때문에 메인 스레드는 블로킹되지 않아 다른 작업을 실행할 수 있게 된다.
[지난 시간: 5 ms] 다른 작업 실행~
[지난 시간: 1008 ms] 결과 반환~ 처리~
위의 실행 결과를 보면 메인 스레드에서 thenAccept()
함수를 호출했음에도 ExecutorService
객체가 작업을 처리하는 동안 다른 작업을 실행하는 것을 확인할 수 있다.