2014-12-31 4 views
19

Необходимо что-то подтвердить. Следующий код:CompletedFuture, supplyAsync() и thenApply()

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

будет такой же, как:

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

Right? Есть ли причина, по которой мы бы использовали thenApply, кроме того: 1) имея большой код для преобразования или 2) нужно повторно использовать лямбда-блок в других местах?

ответ

38

Это не то же самое. Во втором примере, где thenApply не используется, уверен, что вызов convertToB выполняется в том же потоке, что и метод doSomethingAndReturnA.

Но, в первом примере, когда используется метод thenApply, могут случиться другие вещи.

Прежде всего, если CompletableFuture, выполняющий команду doSomethingAndReturnA, завершен, вызов thenApply произойдет в потоке вызывающего абонента. Если CompletableFutures не был завершен, то Function, переданный в thenApply, будет вызываться в той же теме, что и doSomethingAndReturnA.

Confusing? Ну this article might be helpful (спасибо @SotiriosDelimanolis за ссылку).

Я привел короткий пример, иллюстрирующий работу 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"; 
    } 
} 

И выход:

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

Таким образом, когда первая операция медленная (т.е. CompletableFuture еще не завершен) оба вызова происходят в том же потоке. Но если мы должны были удалить Thread.sleep -Call из doSomethingAndReturnA выхода (может) быть такой:

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

Обратите внимание, что convertToB вызова в main потоке.

+1

Этот ответ и этот пост (http://www.deadcoderising.com/java8-writing-asynchronous-code-with -completablefuture /) помогите мне понять CompletableFuture – Jesus

+0

Это все еще не объясняет, почему нужно использовать 'thenApply'? – Yamcha

+0

В первом случае '' '' ForkJoinPool.commonPool-worker-1''' просто зависает там и ждет, пока '' 'doSomethingAndReturnA''' не вернется? – Siddhartha

2

thenApply() - это функция обратного вызова, которая будет выполняться, когда supplyAsync() возвращает значение.

В фрагменте кода 2 поток, вызываемый doSomethingAndReturnA(), ожидает, что функция будет выполнена и вернет данные. Но в некоторых исключениях случаях (например, при вызове webservice и ожидании ответа) поток должен ждать более длительный срок, чтобы получить ответ, который, в свою очередь, потребляет много системных ресурсов (только для ожидания ответа).

Чтобы избежать этого, в будущем возможно выполнение функции обратного вызова, где после вызова doSomethingAndReturnA() отдельный поток позаботится о выполнении doSomethingAndReturnA(), и основной поток вызывающего будет продолжать выполнять другую операцию, не дожидаясь ответ будет возвращен. Как только будет получен ответ doSomethingAndReturnA, будет вызван метод обратного вызова (т. Е. ThenApply())

Смежные вопросы