인프런 커뮤니티 질문&답변

덩더러러쿨님의 프로필 이미지

작성한 질문수

Kevin의 알기 쉬운 RxJava 1부

리액티브 연산자 개요 및 생성 연산자

fromFuture() vs fromCallable() 생성 연산자에 대해

해결된 질문

작성

·

498

0

안녕하세요,

fromFuture() 생성 연산자가 저한테 와 닫지않아 따로 구글링을 해봤는데요,

https://beomseok95.tistory.com/11?category=1029204

해당 링크 글 마지막에 보시면
'fromCallable()과 다른점은 결과 값을 받을 때까지 블로킹한다'라고 설명되어있습니다

그래서 이 각각의 연산자들은 서로 비슷한 연산자인데 역할이 다른 것인지 궁금하고, 각 연산자들의 활용되는 예시가 궁금합니다

* fromFuture() 연산자

* fromCallable() 연산자

답변 2

1

답변 감사합니다!

항상 많은 지식 얻어갑니다!

0

Kevin님의 프로필 이미지
Kevin
지식공유자

안녕하세요? 점심 시간이라서 잠깐 짬내서 답변 드립니다.

시간이 많지가 않아서 간단한 예제로 말씀을 드릴게요.

일단 말씀하신 두개 연산자의 차이점을 설명 드리기 전에 먼저 알아야 될 것은 Callable과 Future인데요.

우리가 Plain Java(이하, Java)에서 쓰레드를 별도로 생성하여 비동기 작업을 실행 시키기 위해서 일반적으로 Runnable 인터페이스를 사용하는데요.

Runnable과 Callable의 차이점은 작업이 끝난 후에 결과 값을 return 하느야 그렇지 않느냐라는건 잘 알고 계실꺼라고 생각합니다.

그런데 Callable의 경우, 비동기적으로 작업이 수행되기 때문에 Callable의 call() 메서드를 호출하는 대상 클래스(일반적으로 main 쓰레드)에서 call() 메서드를 호출한다고해서 return 값을 바로 반환 받을 수 가 없습니다. 왜냐면 예를 들어서 main 쓰레드에서 call() 을 호출하고 별도의 쓰레드에서 작업을 수행하는 동안 main 쓰레드는 이미 종료되어 버릴지도 모르니까요.

그렇기 때문에 Callable 인터페이스를 통해 생성된 별도의 작업 쓰레드가 작업이 끝날때까지 Callable 인터페이스에게 작업을 요청한 대상 쓰레드(일반적으로  main 쓰레드)는 작업 쓰레드의 작업이 끝날때까지 기다렸다가 결과 값을 return 받아야 하는데요. Callable을 통해 생성된 작업 쓰레드의 작업이 끝날때 까지 기다려주는 역할을 하는 것이 바로 Future 인터페이스입니다.

아래에서 Callable과 Future에 대한 예제를 살펴보겠습니다.

==== PlainJavaCallableExample01 ====

public class PlainJavaCallableExample01 {
public static void main(String[] args)
throws ExecutionException, InterruptedException {
Callable<Integer> callable = new SumCallable();
FutureTask futureTask = new FutureTask(callable);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}

public static class SumCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 시간이 오래 걸리는 작업
Thread.sleep(5000L);
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
return sum;
}
}
}

Callable 을 통해 생성된 작업 쓰레드에서 1부터 10까지 더해서 총합을 return 하는 간단한 예제인데요.

시간이 오래 걸리는 작업이라고 가정하고 5초 정도 딜레이 시간을 주었습니다.

위에서 빨간색으로 되어 있는 FutureTask라는 놈이 보이시죠? FutureTask는 Future 인터페이스의 구현체인데 작업 쓰레드가 작업이 끝날때까지 기다려주는 역할을 합니다. 작업 쓰레드에서 작업이 끝날때까지 기다렸다가 futureTask.get()을 통해서 결과 값을 return 받는 것을 보실 수가 있겠습니다.

예제를 하나 더 보겠습니다.

==== PlainJavaCallableExample02 ====

public class PlainJavaCallableExample02 {
public static void main(String[] args)
throws ExecutionException, InterruptedException {
Callable<Integer> callable = new SumCallable();
ExecutorService executorService =
Executors.newSingleThreadExecutor();
Future<Integer> future = executorService.submit(callable);

System.out.println(future.get());

executorService.shutdown();
}

public static class SumCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 시간이 오래 걸리는 작업
Thread.sleep(5000L);
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
return sum;
}
}
}

Callable이나 Runnable 은 ExecutorService 라는 인터페이스와 함께 사용되는 경우가 일반적인데요. ExecutorService 는 쓰레드를 관리해주는 관리자의 역할을 한다고 보시면 되겠습니다.

아무튼 위 코드에서 빨간색으로 된 라인을 보시면 Callable을 통해 생성된 작업 쓰레드를 ExecutorService에 submit 한  후, 실행이 되면 작업이 끝난 후에 Future 를 반환합니다.

이 Future를 통해서 작업 쓰레드의 작업이 끝날때까지 기다렸다가 결과 값을 return 받게 되는것입니다.

자, 그럼 RxJava에서 fromFuture()와 fromCallable()은 언제 사용할 수 있고, 차이점은 무엇일까요?

질문자님께서 말씀하신대로 두 연산자의 역할은 거의 같다라고 보시는게 맞습니다. 다만 fromFuture()는 파라미터로 Future를 입력받고, fromCallable()은 파라미터로 Callable 인터페이스 객체를 입력 받는다는 차이점 밖에 없습니다. 차이점이 너무 싱겁다고 생각드시죠?

그러면 같은 기능을 하는데 왜 굳이 두가지 연산자가 필요하냐는 의문이 드실 수 가 있는데요.

아마도 RxJava가 아닌 Plain Java 코드로 구성되어 있는 레거시 애플리케이션의 코드에서 RxJava의 연산자를 사용해야 될 때가 있을텐데, 같은 프로세스를 코드로 구현하더라도 누구는 Callable을 return하는 API를 만들어 놓았을 수 있고, 다른 누구는 Future를 return하는 API 를 만들어 놓았을 수도 있기때문이지 않을까 싶네요. 즉, 기존의 다양한 레거시 코드에서 RxJava를 사용할 수 있도록 배려한 부분이 있다라는 생각이 듭니다.

단적인 예가 fromIterable()과 fromArray() 연산자라는 생각이 들어요. 배열이나 List나 둘 다 서로 변환이 가능하기 때문에 두 연산자 중에 하나만 있으면 되는건데도 둘 다 만들어 놓은건 연산자를 사용하는 사람들에게 선택의 폭을 넓혀주기 위한거라는 생각이 듭니다.

답변이 충분히 되셨나 모르겠네요.

그럼 저는 그만 오후 업무를 봐야겠습니다. 

좋은 하루 되세요!