체이닝 함수: 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 객체가 작업을 처리하는 동안 다른 작업을 실행하는 것을 확인할 수 있다.




© 2020.08. by assu10

Powered by assu10