Старый программный драйвер позволит вам поиграть в асинхронное программирование CompletableFuture.

Java
Старый программный драйвер позволит вам поиграть в асинхронное программирование CompletableFuture.

Эта статья начинается с примера и представляетCompletableFutureБазовое использование. Но сколько бы вы ни говорили, лучше потренируйтесь сами. Поэтому предлагаю вам ее прочитать, потренироваться на компьютере и быстро освоить.CompletableFuture.

Адрес личного блога:sourl.cn/s5MbCm

Полный текст аннотации:

  • Future VS CompletableFuture
  • CompletableFutureОсновное использование

0x00 Предисловие

В некоторых бизнес-сценариях нам необходимо использовать многопоточное асинхронное выполнение задач для ускорения выполнения задач. Java предоставляетRunnable Future<V>Два интерфейса используются для реализации логики асинхронной задачи.

Несмотря на то чтоFuture<V>Результат выполнения задачи можно получить, но способ получения остается неизменным. мы должны использоватьFuture#getЗаблокируйте вызывающий поток или используйте опрос, чтобы определитьFuture#isDoneТо ли задача окончена, то и получите результат.

Эти два метода обработки не очень элегантны, и библиотека параллельных классов до JDK8 не предоставляла соответствующий метод реализации асинхронного обратного вызова. Ни в коем случае, мы должны использовать сторонние библиотеки классов, такие какGuava, расширенныйFuture, добавьте поддержку функции обратного вызова. Соответствующий код выглядит следующим образом:

Хотя этот метод расширяет возможности асинхронного программирования Java, он по-прежнему не может решить сценарий, в котором несколько асинхронных задач должны зависеть друг от друга.

Чтобы привести пример из жизни, если нам нужно путешествовать, нам нужно выполнить три задачи:

  • Задача 1: Заказать рейс
  • Задача 2: Заказать гостиницу
  • Задача 3: Заказать услугу аренды автомобиля

Очевидно, что задача 1 и задача 2 не связаны между собой и могут выполняться отдельно. Но задача 3 должна дождаться завершения задачи 1 и задачи 2, прежде чем заказывать услугу аренды автомобиля.

Для того, чтобы получить результаты выполнения задачи 1 и задачи 2 при выполнении задачи 3, нам также необходимо использоватьCountDownLatch.

0x01. CompletableFuture

После JDK8 в Java был добавлен очень мощный класс:CompletableFuture. Использование только этого класса может легко выполнить вышеуказанные требования:

Вы можете оставить это в покоеCompletableFutureСвязанныйAPI, о чем будет подробно рассказано ниже.

В сравненииFuture<V>,CompletableFutureПреимущества:

  • Нет необходимости вручную выделять потоки, JDK автоматически выделяет
  • Семантика кода — это понятные асинхронные вызовы цепочки задач.
  • Поддержка оркестрации асинхронных задач

Как, это очень мощно? Далее, держитесь крепче, вот-вот начнется младший черный брат.

1.1 Список методов

Во-первых, давайте посмотрим на методы, предоставляемые этим классом через IDE:

Если немного посчитать, в этом классе всего более 50 методов, боже мой. . .

Но не бойтесь, маленький черный брат поможет вам подвести итоги, будет следовать ритму маленького черного брата и приведет вас к мастерству.CompletableFuture.

Если изображение нечеткое, вы можете обратить внимание на «Общение программы» и ответить: «233», чтобы получить карту разума.

1.2 Создайте экземпляр CompletableFuture

СоздайтеCompletableFutureЭкземпляры объектов мы можем использовать следующими методами:

Первый метод создаетCompletableFuture, это не о чем говорить. Мы сосредоточимся на следующих четырех асинхронных методах.

первые два методаrunAsyncВозвращаемые значения не поддерживаются, в то время какsupplyAsyncМожет поддерживать возврат результатов.

По умолчанию эти два метода будут использовать публичныйForkJoinPoolВыполнение пула потоков, количество потоков по умолчанию в этом пуле потоков равноCPUколичество ядер.

Вы можете установить параметр JVM:-Djava.util.concurrent.ForkJoinPool.common.parallelism, чтобы установить количество потоков в пуле потоков ForkJoinPool.

Использование общего пула потоков будет иметь недостаток: как только задача будет заблокирована, это приведет к тому, что другие задачи не смогут выполниться. таксильныйРекомендуется использовать последние два метода Активно создавать пул потоков в соответствии с различными типами задач, чтобы изолировать ресурсы и избежать взаимного вмешательства.

1.3 Установите результат задачи

CompletableFutureПредоставляет следующие методы для упреждающей установки результатов задачи.

 boolean complete(T value)
 boolean completeExceptionally(Throwable ex)

Первый способ, активная настройкаCompletableFutureРезультат выполнения задачи, если возвращаетсяtrue, указывая на то, что настройка прошла успешно. если вернутьсяfalse, настройка не удалась, так как задача была выполнена и уже есть результат выполнения.

Пример кода выглядит следующим образом:

// 执行异步任务
CompletableFuture cf = CompletableFuture.supplyAsync(() -> {
  System.out.println("cf 任务执行开始");
  sleep(10, TimeUnit.SECONDS);
  System.out.println("cf 任务执行结束");
  return "楼下小黑哥";
});
//
Executors.newSingleThreadScheduledExecutor().execute(() -> {
  sleep(5, TimeUnit.SECONDS);
  System.out.println("主动设置 cf 任务结果");
  // 设置任务结果,由于 cf 任务未执行结束,结果返回 true
  cf.complete("程序通事");
});
// 由于 cf 未执行结束,将会被阻塞。5 秒后,另外一个线程主动设置任务结果
System.out.println("get:" + cf.get());
// 等待 cf 任务执行结束
sleep(10, TimeUnit.SECONDS);
// 由于已经设置任务结果,cf 执行结束任务结果将会被抛弃
System.out.println("get:" + cf.get());
/***
   * cf 任务执行开始
   * 主动设置 cf 任务结果
   * get:程序通事
   * cf 任务执行结束
   * get:程序通事
*/

Здесь следует отметить одну вещь: когда-тоcompleteустановлено успешно,CompletableFutureВозвращаемый результат не будет изменен, даже если последующиеCompletableFutureВыполнение задачи заканчивается.

Второй способ, дающийCompletableFutureУстановите объект исключения. Если настройка прошла успешно, если вызовgetПодождите, пока метод получит результат, он выдаст ошибку.

Пример кода выглядит следующим образом:

// 执行异步任务
CompletableFuture cf = CompletableFuture.supplyAsync(() -> {
    System.out.println("cf 任务执行开始");
    sleep(10, TimeUnit.SECONDS);
    System.out.println("cf 任务执行结束");
    return "楼下小黑哥";
});
//
Executors.newSingleThreadScheduledExecutor().execute(() -> {
    sleep(5, TimeUnit.SECONDS);
    System.out.println("主动设置 cf 异常");
    // 设置任务结果,由于 cf 任务未执行结束,结果返回 true
    cf.completeExceptionally(new RuntimeException("啊,挂了"));
});
// 由于 cf 未执行结束,前 5 秒将会被阻塞。后续程序抛出异常,结束
System.out.println("get:" + cf.get());
/***
 * cf 任务执行开始
 * 主动设置 cf 异常
 * java.util.concurrent.ExecutionException: java.lang.RuntimeException: 啊,挂了
 * ......
 */

1.4 CompletionStage

CompletableFutureРеализовать два интерфейса соответственноFutureиCompletionStage.

FutureС интерфейсом все знакомы, здесь речь в основном идет оCompletionStage.

CompletableFutureБольшинство методов исходят изCompletionStageинтерфейс, именно из-за этого интерфейса,CompletableFutureЕсть такая мощная функция.

хочу понятьCompletionStageИнтерфейс, нам нужно понять временные отношения задач в первую очередь. Мы можем разделить временные отношения задачи на следующие типы:

  • отношения последовательного выполнения
  • Отношения параллельного выполнения
  • И отношения агрегации
  • ИЛИ отношение агрегации

1.5 Отношения последовательного исполнения

Задачи выполняются последовательно, и следующая задача должна дождаться завершения предыдущей задачи, прежде чем продолжить.

CompletionStageСуществует четыре группы интерфейсов, которые могут описывать последовательные отношения, а именно:

thenApplyМетод должен передать основной параметр какFunction<T,R>тип. Основные методы этого класса:

 R apply(T t)

Таким образом, этот интерфейс примет результат, возвращенный предыдущей задачей, в качестве входного параметра, и результат будет возвращен, когда выполнение завершится.

thenAcceptМетод должен передать объект параметра какConsumer<T>Type, основными методами этого класса являются:

void accept(T t)

возвращаемое значениеvoidВидно, что этот метод не поддерживает возврат результатов, а должен передавать в качестве параметра результат выполнения предыдущей задачи.

thenRunМетод должен передать объект параметра какRunnableType, этот класс должен быть всем знаком, метод ядра не поддерживает входящие параметры и не возвращает результаты выполнения.

thenComposeметод иthenApplyто же, толькоthenComposeнужно вернуть новыйCompletionStage. Это понимание относительно абстрактно и может быть понято вместе с кодом.

метод сAsync, что означает, что он может выполняться асинхронно. В этой серии также есть перегруженные методы, которые можно передать в пользовательский пул потоков. Рисунок выше не показан, и читатели могут только просматривать API самостоятельно.

Наконец, мы показываем кодthenApplyКак пользоваться:

CompletableFuture<String> cf
        = CompletableFuture.supplyAsync(() -> "hello,楼下小黑哥")// 1
        .thenApply(s -> s + "@程序通事") // 2
        .thenApply(String::toUpperCase); // 3
System.out.println(cf.join());
// 输出结果 HELLO,楼下小黑哥@程序通事

Этот код относительно прост, сначала мы запускаем асинхронную задачу, а затем последовательно выполняем две последующие задачи. Задача 2 должна дождаться завершения задачи 1, а задача 3 — дождаться выполнения задачи 2.

Вышеупомянутый метод, вы должны помнитьFunction<T,R>,Consumer<T>,RunnableТри разные, выбирайте и используйте в зависимости от сцены.

1.6 И отношения агрегации

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

Как показано выше, задача C не начнет выполняться, пока не будут выполнены и задача A, и задача B.

CompletionStageСуществуют следующие интерфейсы, описывающие эту связь.

thenCombineОсновные параметры методаBiFunction, рольFunctionто же, толькоBiFunctionможет принимать два параметра иFunctionМожно принять только один параметр.

thenAcceptBothОсновные параметры методаBiConsumerтакже работает сConsumerТо же самое, но он должен принимать два параметра.

runAfterBothОсновные параметры метода самые простые, они были введены выше и повторно вводиться не будут.

Эти три группы методов могут выполнять только две задачи И отношения агрегации.Если вам нужно выполнить несколько отношений агрегации задач, вам нужно использоватьCompletableFuture#allOf, но здесь следует отметить, что этот метод не поддерживает возврат результатов задачи.

Пример кода, связанный с отношением агрегации И, был использован в начале, и я вставлю его сюда для вашего удобства:

1.7 Отношение агрегации ИЛИ

Существует отношение агрегации И, и, конечно же, есть отношение агрегирования ИЛИ. Отношение агрегации ИЛИ означает, что пока любая из нескольких задач завершена, следующая задача может быть выполнена следующей.

CompletionStageСуществуют следующие интерфейсы, описывающие эту связь:

Первые три группы интерфейсных методов передаются для участия в отношении агрегации И и не будут здесь подробно объясняться.

Конечно, можно использовать отношение агрегации ИЛИ.CompletableFuture#anyOfВыполняйте несколько задач.

В следующем примере кода показано, как использоватьapplyToEitherЗавершите отношение ИЛИ.

CompletableFuture<String> cf
        = CompletableFuture.supplyAsync(() -> {
    sleep(5, TimeUnit.SECONDS);
    return "hello,楼下小黑哥";
});// 1

CompletableFuture<String> cf2 = cf.supplyAsync(() -> {
    sleep(3, TimeUnit.SECONDS);
    return "hello,程序通事";
});
// 执行 OR 关系
CompletableFuture<String> cf3 = cf2.applyToEither(cf, s -> s);

// 输出结果,由于 cf2 只休眠 3 秒,优先执行完毕
System.out.println(cf2.join());
// 结果:hello,程序通事

1.8 Обработка исключений

CompletableFutureЕсли во время выполнения метода возникает исключение, при вызовеget,joinИсключение выдается только при получении результата задачи.

Приведенный выше код мы показываем с помощьюtry..catchОбработайте указанное выше исключение. Но это не очень элегантно,CompletionStageПредоставляет несколько методов для изящной обработки исключений.

exceptionallyИспользуйте что-то вродеtry..catchсерединаcatchОбработка исключений в блоках кода.

whenCompleteиhandleметод похож наtry..catch..finanllyсерединаfinallyкодовый блок. Он будет выполнен независимо от того, возникнет ли исключение. Разница между двумя методами заключается в том, чтоhandleПоддержка возврата результатов.

Пример кода ниже показываетhandleиспользование:

CompletableFuture<Integer>
        f0 = CompletableFuture.supplyAsync(() -> (7 / 0))
        .thenApply(r -> r * 10)
        .handle((integer, throwable) -> {
            // 如果异常存在,打印异常,并且返回默认值
            if (throwable != null) {
                throwable.printStackTrace();
                return 0;
            } else {
                // 如果
                return integer;
            }
        });


System.out.println(f0.join());
/**
 *java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
 * .....
 * 
 * 0
 */

0x02.Сводка

JDK8 обеспечиваетCompletableFutureФункция очень мощная, она может организовывать асинхронные задачи, завершать последовательное выполнение, параллельное выполнение, отношения агрегации И, отношения агрегации ИЛИ.

Однако методов в этом классе слишком много, а методы еще и нужно передавать в различные функциональные интерфейсы, новички прямо запутаются, когда начнут их использовать. Здесь, чтобы помочь вам обобщить роль трех типов основных параметров

  • FunctionЭтот тип функционального интерфейса поддерживает как получение параметров, так и возврат значений.
  • ConsumerЭтот тип функции интерфейса поддерживает только прием параметров и не поддерживает возвращаемые значения.
  • RunnableЭтот тип интерфейса не поддерживает прием параметров и не поддерживает возвращаемые значения.

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

Наконец, вставьте еще раз карту разума в начале статьи, надеюсь, она вам поможет.

0x03 Справочная документация

  1. Geek Time — Колонка параллельного программирования
  2. коло not.com/2016/02/29/…
  3. Woohoo. IBM.com/developer Я…

Последнее слово (пожалуйста, обратите внимание)

CompletableFutureЯ обратил на это внимание давно, я думал, что это последуетFutureКроме того, им очень легко пользоваться, но кто знает, как сложно его освоить. Различные методы API выглядят немного большими.

Позже я увидел Geek Time - колонку "Параллельное программирование" с использованием индуктивной классификации.CompletableFutureРазличные методы, все сразу поняли. Так что эта статья также относится к этому методу индукции.

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

Ради тяжелой работы, которую написал Сяо Хей, пожалуйста, обратите внимание и поставьте лайк. Не будь уверен в следующий раз, старший брат! Писать статьи очень сложно, и вам нужны положительные отзывы.

Если вам не хватает таланта и обучения, неизбежно будут ошибки.Если вы обнаружите какие-либо ошибки, пожалуйста, оставьте сообщение и укажите на это мне, и я его исправлю.

Спасибо за прочтение,Я придерживаюсь оригинала, добро пожаловать и спасибо за внимание~

Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: программа для общения, ежедневный толчок галантерейных товаров. Если вас интересует мой рекомендуемый контент, вы также можете подписаться на мой блог:studyidea.cn