RATSENO

CompletableFuture<T> -2 본문

DEV/JAVA

CompletableFuture<T> -2

RATSENO 2021. 3. 22. 19:47

ratseno.tistory.com/100

 

CompletableFuture -1

CompletableFuture를 간단하게 정리하기 전에, 속성으로 동기, 비동기에 대해서 정리하겠습니다. 저도 이해하기 편하도록 식당을 예를 들어 설명해보겠습니다. 동기 : 식당에 갔습니다. 음식을 점원

ratseno.tistory.com

이전 포스팅에 이어서,  논블로킹 방식으로 사용하기 위한 콜백 방식에 대해서 정리해보겠습니다.

 

이전까지는 비동기로 호출하였지만, get() 또는 join()을 호출함으로써

응답 값을 받을 때까지 블로킹이 되었습니다.

 

하지만 이러한 동작은 저희가 원하는 모습이 아닙니다.

흐름을 방해 받고 싶지 않습니다.

그래서 저희는 응답값이 리턴되면, 어떠한 행위를 하도록 정의할 것입니다.

이러한 것을 콜백 방식이라고 합니다.

 

프런트엔드 개발 시에 많이 쓰이기 때문에 익숙하실 것 같습니다.

예를 들자면

window.setTimeout(function(){
    console.log("3초 뒤에 실행");
}, 3000);

이러한 모습이라던가, 

또는 이어서 보여드릴 메서드와 유사하게 생긴 Promise()의 .then() 메서드가 있습니다.

ratseno.tistory.com/22?category=781442

 

[JS]Promise (프라미스) - 1

프라미스 프라미스는 콜백의 단점을 해결하려는 시도 속에서 만들어졌습니다. 프로미스는 일반적으로 안전하고 관리하기 쉬운 코드를 만들 수 있게 됩니다. 프라미스가 콜백을 대체하는 것은

ratseno.tistory.com

//3초뒤에 1을 return
function asyncTest(){
    return new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(1);
        }, 3000);
    });
};

asyncTest()
.then(function(res){
	//3초뒤에 return받은 1을 사용
    console.log(1+"을 받았습니다.");
});

//1을 받았습니다

 


CompletableFuture<Void> thenAccept(Consumer<? super T> action)

위에서 잠깐 살펴보았던 Promise의 .then() 메서드와 아주 유사하게 생겼습니다.

사용법도 동일합니다. 비동기로 호출한 메서드가 실행되고 결과값이 리턴 또는 동작이 완료되었을 때,

실행될 콜백 함수를(Consumer <? super T> action) 정의하면 됩니다.

리턴 타입에서 볼 수 있듯이 thenAccept는 Void 리턴 값이 없습니다.

    @Test
    public void 비동기_방식_논블록킹_되는_콜백방식_ID조회하기_thenAccept(){

        CompletableFuture<Void> completableFuture = memberService.getMemberIdAsync_supplyAsync("철수")
                .thenAccept(integer -> {
                    log.info("리턴값이 없는 thenAccept");
                    log.info("회원 ID:" + integer);
                });
        log.info("아직 최종 데이터를 전달 받지는 않았지만, 다른작업 수행가능");

        /*메인 스레드가 끝나는것을 방지하기 위한 코드*/
        Assert.assertNull(completableFuture.join());
    }

supplyAsync() 메서드를 호출하여 3초 뒤 값이 리턴되면, log를 찍는 콜백 함수입니다.

해당 샘플에서는 리턴 값을 사용하기 위해 supplyAsync()를 사용하였지만, runAsync()도 사용 가능합니다.


<U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)

thenApplythenAccept와 다르게 리턴 값이 있는 메서드입니다. thenAccept와 함께 사용 가능합니다.

    @Test
    public void 비동기_방식_논블록킹_되는_콜백방식_ID조회하기_thenApply(){

        CompletableFuture<Void> completableFuture = memberService.getMemberIdAsync_supplyAsync("철수")
                .thenApply(integer -> {
                    log.info("리턴값이 있는 thenApply");
                    log.info("회원 ID:" + integer);
                    return integer;
                })
                .thenAccept(result -> {
                    log.info("리턴값이 없는 thenAccept");
                    log.info("회원 ID:" + result);
                    Assert.assertEquals(1, result.intValue());
                });
        log.info("아직 최종 데이터를 전달 받지는 않았지만, 다른작업 수행가능");

        /*메인 스레드가 끝나는것을 방지하기 위한 코드*/
        Assert.assertNull(completableFuture.join());
    }

 


public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)

각기 다른 소요시간이 걸리는 비동기 메서드가 전부 완료되었을 때 실행되는 allOf()도 있습니다.

    @Test
    public void test(){
        CompletableFuture<Integer> temp = memberService.getMemberIdAsync("철수");

        CompletableFuture completableFuture1 = CompletableFuture.supplyAsync(() -> {
            log.info("future-1");
            return (Integer)1;
        });

        CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(() -> {
            log.info("future-2");
            return (Integer)2;
        });

        CompletableFuture completableFuture3 = CompletableFuture.supplyAsync(() -> {
            log.info("future-3");
            return (Integer)3;
        });

        List<CompletableFuture> futures = Arrays.asList(completableFuture1,
                                                        completableFuture2,
                                                        completableFuture3);
        CompletableFuture.allOf(completableFuture1, completableFuture2, completableFuture3)
                .thenAccept(s -> {
                    List<Object> result = futures.stream()
                            .map(pageContentFuture -> pageContentFuture.join())
                            .collect(Collectors.toList());
                    log.info(result.toString());
                });

        /*메인 스레드가 끝나는것을 방지하기 위한 코드*/
        Assert.assertEquals(1, temp.join().intValue());
    }

간단하게 CompletableFuture의 사용법에 대해서 정리해 보았습니다.

아직 간단하게 사용한 경험밖에 없지만, 능숙하게 사용하게 된다면 유용한 기능일 것 같습니다.

동기, 비동기, 블로킹, 논블로킹에 대해서 좀더 공부해야겠다는 생각이 들었습니다...ㅠ

 

부족한 포스팅 봐주셔서 감사합니다. 언제나 지적은 감사하게 받겠습니다!

참고 : www.baeldung.com/java-completablefuture

           brunch.co.kr/@springboot/267

           docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html

 

github : github.com/RATSENO/CompletableFuture-Test

 

 

 

Comments