Три угла, чтобы получить CompletableFuture (1)

Java

Пусть в мире не будет сложной Java для изучения

Нравится, Следуй, Избранное

Введение

  • Многопоточное программирование является обычным явлением в Java-разработке.С самого начала Thread, Runnable to Future и затем CompletableFuture JDK использует многопоточность для постоянного расширения функций для нас.
  • О CompletableFuture написано много вводных статей и руководств, так зачем писать эту статью? Существует довольно много руководств, но много копий и вставок или нагромождений API. Смотреть на это полностью сложно.
  • Следовательно, эта статья в основном рассказывает о полноценном выравнивании с точки зрения вашего собственного обучения и расширяет полномочия из трех основных линий, чтобы вы могли понять завершительную культуру с четкого уровня.

2. Механизм «Начало из будущего»

  • Механизм Future был представлен в Java 1.5 и представляет собой результат асинхронных вычислений. О Future/Callable можно обратиться к

    nuggets.capable/post/684490…, чтобы у вас сразу появилось чувство иерархии. Не все, но кажется довольно запутанным.

  • Future решает проблему, заключающуюся в том, что Runnable не имеет возвращаемого значения, и предоставляет два вида get() для получения результата.

public interface Future<V>{
  
  //堵塞获取
  V get() throws InterruptedException,ExecutionException;
  
  //等待timeout获取
  V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException;
}

3. Полное будущее родился

  • Метод блокировки получения результатов явно противоречит асинхронному программированию, метод опроса по тайм-ауту не элегантен и не позволяет вовремя получить результаты вычислений. Многие языки предоставляют обратные вызовы для асинхронного программирования, например Node.js. Некоторые библиотеки классов Java, такие как netty, guava и т. д., также расширяют Future, чтобы улучшить возможности асинхронного программирования. Официальная явно не отстает: в Java 8 добавлены CompleteFuture и связанные с ним API, что значительно расширяет возможности Future.

//接受Runnable,无返回值,使用ForkJoinPool.commonPool()线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)

//接受Runnable,无返回值,使用指定的executor线程池  
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
  
//接受Supplier,返回U,使用ForkJoinPool.commonPool()线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
  
//接受Supplier,返回U,使用指定的executor线程池 
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
  
  • Мы видим, как CompleteFuture достигает интерфейсов Future, CompletionStage2. Будущее, которое мы все знаем, в основном используется для реализации асинхронного события, которое не должно запускаться.

3.1 Создайте новый CompletableFuture

  • runAsync не возвращает значения

  • SupplyAsync имеет возвращаемое значение

3.2. Стадия завершения

  • Сцена значит сцена. Как следует из названия, CompletionStage представляет собой этап синхронного или асинхронного вычисления и ссылку на конечный результат. Несколько этапов завершения образуют сборочную линию, и сборка одного звена может быть передана следующему звену. Результату может потребоваться пройти через несколько этапов завершения.

  • Например, в очень распространенном сценарии обмена сначала списываются баллы, затем отправляется текстовое сообщение, а затем устанавливается логотип персональной домашней страницы. .

  • CompletionStage обеспечивает поддержку последовательного вызова CompletableFuture, а thenApply().thenApply()... CompletionStage связывается один за другим.

  • Simple CompletionStage: после выполнения асинхронной задачи SupplyAsync() используйте thenApply() для передачи результата на следующий этап.

Четыре, три угла для анализа CompletableFuture

  • С введением двух предварительных концепций CompletableFuture и CompletionStage мы можем формально понять CompletableFuture с трех точек зрения.

4.1 Последовательные отношения

  • thenApply
//这是最简单也是最常用的一个方法,CompletableFuture的链式调用
  public static void main(String[] args) {

    CompletableFuture<String> completableFuture = CompletableFuture.runAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("hello");
      }catch (Exception e){
        e.printStackTrace();
      }
    }).thenApply(s1 -> {
      System.out.println(" big");
      return s1 + "big";
    }).thenApply(s2 -> " world");
  }

//hello big world
  • thenRun

    • Когда вычисление завершено, выполняется Runnable, а результат вычисления CompletableFuture не используется.Предыдущий результат вычисления CompletableFuture также будет проигнорирован, и будет возвращен тип CompletableFuture.
  • thenAccept

    • thenApply и thenAccept похожи, разница в том, что thenAccept является чистым потреблением, не имеет возвращаемого значения и поддерживает цепочку вызовов.
 CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(5);
        return "success";
      } catch (Exception e) {
        e.printStackTrace();
        return "error";
      }
    }).thenAcceptAsync(s->{
      if ("success".equals(s)){
        System.out.println(s);
      }else {
        System.err.println(s);
      }
    });
  • thenCompose
    • Оба принимают функцию, входные данные — вычисленное значение текущего CompleteableFuture и возвращают новый CompletableFuture. То есть этот новый CompleteableFuture объединяет исходный CompleteableFuture с CompleteableFuture, возвращаемым функцией.
    • Обычно используется, когда второй CompleteableFuture должен использовать результат первого CompleteableFuture в качестве входных данных.
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor)
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 1)
                     .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
 thenApply、thenCompose都接受一个Function的函数式接口,那么区别呢?
 1.thenApply使用在同步mapping方法。
 2.thenCompose使用在异步mapping方法。
  • 🌰 вот и мы
//thenApply
thenApply(Function<? super T,? extends U> fn)

//thenCompose
thenCompose(Function<? super T,? extends CompletableFuture<U>> fn)
  
  
//可以看出thenApply返回的是一个对象U,而thenCompose返回的是CompletableFuture<U>
//从使用角度是来看,如果你第二个操作也是异步操作那么使用thenCompose,如果是一个简单同步操作,那么使用thenApply,相信实战几次你就能明白什么时候使用thenApply、thenCompose。

4.2, И отношения

  • thenCombine

    • Судя по моему уровню 6, «комбинировать» и «компоновать», похоже, имеют значение «комбинировать» и «комбинировать», так в чем же разница?
    • Основное отличие состоит в том, что связывание двух полномочий Fenchombine не зависит от зависимостей, а второе рейтинговое завершение.
    • Многократное завершение комбинации 1ncombine является независимым, но весь конвейер синхронизирован.

    🌰 вот и мы

//从上图可以看出,thenCombine的2个CompletableFuture不是依赖关系,第二个CompletableFuture比第一个CompletableFuture先执行,最后的BiFunction()组合2个CompletableFuture结果。

//再次强调:整个流水线是同步的
  • thenAcceptBoth
//2个CompletableFuture处理完成后,将结果进行消费。
//与thenCombine类似,第2个CompletableFuture不依赖第1个CompletableFuture执行完成,返回值Void。

  • runAfterBoth
    • После завершения двух CompletableFuture будет выполнен Runnable. Это похоже на thenAcceptBoth и thenCombine, но разница в том, что runAfterBoth не заботится о возвращаемом значении любого CompletableFuture, пока CompletableFuture выполняется, он будет работать, и он также не имеет возвращаемого значения.
  • allOf
    • Просто дождитесь завершения всех CompletableFuture и верните CompletableFuture.
public static CompletableFuture<Void>  allOf(CompletableFuture<?>... cfs)
  public static void main(String[] args) throws Exception {
    long start = System.currentTimeMillis();
    CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(1);
        System.out.println("i am sleep 1");
      } catch (Exception e) {
        e.printStackTrace();
      }
      return "service 1";
    });
    CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(2);
        System.out.println("i am sleep 2");
      } catch (Exception e) {
        e.printStackTrace();
      }
      return "service 2";
    });

    CompletableFuture<String> completableFuture3 = CompletableFuture.supplyAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("i am sleep 3");
      } catch (Exception e) {
        e.printStackTrace();
      }
      return "service 3";
    });
    CompletableFuture<Void> completableFuture = CompletableFuture
        .allOf(completableFuture1, completableFuture2, completableFuture3);
    completableFuture.join();
    System.out.println(System.currentTimeMillis() - start);
  }

4.3, отношение ИЛИ

  • applyToEither
    • Результат самого быстрого выполнения CompletableFuture используется как входной результат следующего этапа.
  • acceptEither
    • Когда завершится самое быстрое выполнение CompletableFuture, будет выполнено потребление действия.
  • runAfterEither
    • После завершения любого CompletableFuture будет выполнен следующий Runnable.
  • anyOf
    • Функция AnyOf возвращает значение после завершения любого CompletableFuture.

резюме

  • CompletableFuture значительно расширяет возможности Future и упрощает асинхронное программирование.
  • В этой статье в основном рассматриваются этапы между CompletableFutures с точки зрения И, ИЛИ и сериализации. Разные сцены должны накладываться на разные этапы.