본문 바로가기
Language/Java

[Java 8] CompletableFuture - 4 (종료조건)

by ocwokocw 2021. 2. 11.

- 출처: 자바 8 in action

- CompletableFuture 종료

앞서 작성한 코드들에서 원격 서비스를 흉내내기 위해 1초 sleep을 주었지만 사실 실제 상황에서는 네트워크 상황이나 다른 변수등 어떤일이 일어날지 알 수 없다. 그래서 원격 서비스를 사용할 때 더 실제와 같은 상황을 부여하기 위해 랜덤하게 sleep을 할당해보자.

 

getTicketPrice 메소드에 sleep 부분을 아래 코드로 변경하자.

 

int delay = 500 + random.nextInt(2000); 
		
try {
	Thread.sleep(delay);
} catch (InterruptedException e) {
	throw new RuntimeException(e);
}

 

앞선 코드는 모든 가격이 조회될때까지 기다렸지만 더 빨리 조회되는 가격정보를 먼저 보여줄 수 있다. 그러려면 CompletableFuture의 Stream을 직접 제어해야 하는데 아래와 같이 Stream을 반환하는 메소드를 추가해보자.

 

public Stream<CompletableFuture<String>> getTicketPriceStream(List<Ticket> tickets){
	
	return tickets.stream()
			.map(ticket -> CompletableFuture.supplyAsync(() -> 
				getTicketPrice(ticket.getFrom(), ticket.getTo())))
			.map(future -> future.thenApply(DiscountForm::new))
			.map(future -> future.thenCompose((discountForm) -> 
					CompletableFuture.supplyAsync(() -> 
						DiscountService.getPriceViaDiscountForm(discountForm)
					)));  
}

 

Stream을 반환받으면 아래와 같은 형식으로 사용할 수 있다.

 

CompletableFuture[] futures = airline.getTicketPriceStream(tickets)
	.map(f -> f.thenAccept(System.out::println))
	.toArray(size -> new CompletableFuture[size]);
	 
CompletableFuture.allOf(futures).join();

 

thenAccept는 CompletableFuture에 등록된 동작을 소비하는 메소드이다. 그렇다고 thenAccept 메소드만 코딩 후 그대로 실행한다고 해서 결과값이 출력되는 것은 아니다. thenAccept 는 등록된 동작을 어떻게 소비할지만 정의한 상태인것이다. thenAccept는 Comsumer 함수형 인터페이스를 인자로 받으므로 CompletableFuture<Void> 형을 반환하게 된다.

 

소비하는 동작까지 정했으면 실제로 어떻게 CompletableFuture를 종료할것인지를 정해야 한다. 만약 모든 ticket의 가격을 구하고 싶다면 CompletableFuture.allOf 메소드로 모든 ticket의 비동기 연산이 끝날때까지 기다린다. 얼만큼의 시간이 걸렸는지 더 자세히 파악하기 위해 메소드를 수정해보자.

 

long startTime = System.currentTimeMillis();
	
CompletableFuture[] futures = airline.getTicketPriceStream(tickets)
	.map(f -> f.thenAccept(resultPrice -> {
		long end = System.currentTimeMillis();
		String result = "Date: " + ((end - startTime) / 1000.0) + ", result: " + resultPrice;
		System.out.println(result);
	}))
	.toArray(size -> new CompletableFuture[size]);

 

- 실행결과

 

Date: 1.8, result: KOR,KAZ,0.6158781006883716
Date: 1.825, result: KOR,JPN,0.31299183537878195
Date: 1.951, result: KOR,CHN,0.9042009738962702
Date: 2.018, result: KOR,CHE,0.6109196728742164
Date: 2.836, result: KOR,USA,0.6322615503251896

 

랜덤으로 수행해서 각 Ticket 마다 가격계산을 완료한 시간이 다르다.

만약 모든 결과를 기다릴 필요 없이 1개의 결과만 받으면 되는 상황이라면 allOf 대신 anyOf를 사용한다.

 

CompletableFuture.anyOf(futures).join();

 

- 실행결과

 

Date: 1.586, result: KOR,KAZ,0.571177037860831

댓글