2014-12-31 18 views
19

Potrzeba potwierdzenia czegoś. Poniższy kod:CompletableFuture, supplyAsync() and thenApply()

CompletableFuture 
    .supplyAsync(() -> {return doSomethingAndReturnA();}) 
    .thenApply(a -> convertToB(a)); 

byłaby taka sama, jak:

CompletableFuture 
    .supplyAsync(() -> { 
     A a = doSomethingAndReturnA(); 
     convertToB(a); 
}); 

prawda? Czy istnieje jakikolwiek powód, dla którego użylibyśmy thenApply oprócz: 1) posiadania dużego kodu do konwersji lub 2) ponownego użycia bloku lambda w innych miejscach?

Odpowiedz

38

To jest to nie to samo, co. W drugim przykładzie, w którym nie jest używane thenApply, jest pewne, że wywołanie convertToB jest wykonywane w tym samym wątku, co metoda doSomethingAndReturnA.

Ale w pierwszym przykładzie, gdy używana jest metoda thenApply, mogą się zdarzyć inne rzeczy.

Po pierwsze, jeśli CompletableFuture, który wykona doSomethingAndReturnA, zakończy się, wywołanie thenApply nastąpi w wątku wywołującego. Jeśli wartość CompletableFutures nie została zakończona, zostanie przekazana Function do thenApply w tym samym wątku, co doSomethingAndReturnA.

Mylące? Dobrze this article might be helpful (dzięki @SotiriosDelimanolis za link).

Podałem krótki przykład ilustrujący działanie thenApply.

public class CompletableTest { 
    public static void main(String... args) throws ExecutionException, InterruptedException { 
     final CompletableFuture<Integer> future = CompletableFuture 
       .supplyAsync(() -> doSomethingAndReturnA()) 
       .thenApply(a -> convertToB(a)); 

     future.get(); 
    } 

    private static int convertToB(final String a) { 
     System.out.println("convertToB: " + Thread.currentThread().getName()); 
     return Integer.parseInt(a); 
    } 

    private static String doSomethingAndReturnA() { 
     System.out.println("doSomethingAndReturnA: " + Thread.currentThread().getName()); 
     try { 
      Thread.sleep(1000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     return "1"; 
    } 
} 

a wyjście jest:

doSomethingAndReturnA: ForkJoinPool.commonPool-worker-1 
convertToB: ForkJoinPool.commonPool-worker-1 

Więc, kiedy pierwsza operacja jest powolna (czyli CompletableFuture nie jest jeszcze zakończone) oba połączenia występują w tym samym wątku. Ale jeśli byliśmy usunąć Thread.sleep-Call z doSomethingAndReturnA wyjście (mogą) być tak:

doSomethingAndReturnA: ForkJoinPool.commonPool-worker-1 
convertToB: main 

Zauważ, że convertToB połączenie jest w wątku main.

+1

Ta odpowiedź i ten post (http://www.deadcoderising.com/java8-writing-asynchronous-code-with -completablefuture /) pomoże mi zrozumieć CompletableFuture – Jesus

+0

To nadal nie wyjaśnia, dlaczego można by użyć 'thenApply'? – Yamcha

+0

W pierwszym przypadku, czy '' 'ForkJoinPool.commonPool-worker-1''' po prostu zawiesi się i czeka na powrót do' '' doSomethingAndReturnA'''? – Siddhartha

2

thenApply() to funkcja wywołania zwrotnego, która zostanie wykonana, gdy funkcja supplyAsync() zwróci wartość.

W fragmencie kodu 2 wątek, który wywołał funkcję doSomethingAndReturnA(), czeka na wykonanie funkcji i zwrócenie danych. Ale w niektórych wyjątkowych przypadkach (takich jak wywoływanie usługi sieci Web i oczekiwanie na odpowiedź), wątek musi czekać na dłuższy czas, aby uzyskać odpowiedź, która z kolei zużywa wiele zasobów systemowych (tylko na oczekiwanie na odpowiedź). Aby uniknąć tej sytuacji, w pełni dostępna jest funkcja wywołania zwrotnego, w której po wywołaniu metody doSomethingAndReturnA() zostanie wywołany osobny wątek wykonywania instrukcji doSomethingAndReturnA(), a główny wątek wywołujący będzie kontynuował wykonywanie innej operacji bez oczekiwania na odpowiedź zostanie zwrócona. Po uzyskaniu odpowiedzi doSomethingAndReturnA, metoda wywołania zwrotnego zostanie wywołana (tj. WtedyApply())