С CompletableFuture, что делает асинхронное программирование не так сложно!

В данной статье ОБЗОР:

  • Сценарий Бизнес спрос описывает
  • Технический дизайн мышления
  • Будущие фактические шаблоны проектирования
  • боевой режим CompletableFuture
  • производство предложение CompletableFuture
  • тест производительности CompletableFuture
  • CompletableFuture Extended

1, сценарий бизнес спроса описывает


То же самое постоянно меняется.

Предположительно, каждый в свое свободное время, часто смотреть видео, часто с несколькими АРР, таких как Youku, любовь Фантастическая искусство, Tencent.

Это видео может быть воспроизведено только APP по телефону, но и поддерживать воспроизведение на телевизоре.

APP играл на телевидении терминала независимых релизов, с конца телефона APP не то же самое.

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

Давайте посмотрим на визуальные эффекты.

Tencent видео страница детали альбома по телефону:

End мобильных телефонов альбом подробно страница

Верхняя половина выстрелов, рекомендуется следующее для вас, звезда актера, периферические рекомендации, комментарии и другие функции.

Соответственно, в конце страницы детали альбома ТВ - шоу не таким же образом. Продакт - менеджер предложил гипотезу необходимо пересмотреть из требований страницы для деталей.
Требования к стилю , как показано ниже:

TV конец детали альбома страница

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

Требования к разработке продукта является то, что некоторые пластины от рекомендуемого содержания, а некоторые плиты из поиска, источник некоторых секторов CMS (системы управления контентом). Просто чтобы понять, для каждого содержания раздела из различных источников, полученное из рекомендации, поиска и другой содержание интерфейса требования рядом запросов в режиме реального времени.

2, технический дизайн мышление


Принимая во внимание этот продукт спроса упоминания, на самом деле, не трудно осуществить.

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

Статический дизайн интерфейса:

Сам альбом является атрибутивные данные и видео альбомы под как правило , не меняются часто.
Сценарий спроса во введении, я снимал канал кино. Если ТВ канал, будет показан список эпизодов (все видео в альбоме, такие как Episode 1, Episode 2 ...), а также обновлять видео , как правило , менее часто, так что данные в детали альбома страница Список эпизодов вы можете получить от статического интерфейса.

Статический интерфейс процесс генерации данных:

Статический дизайн интерфейса

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

Динамический дизайн интерфейса:

Вариант первый:

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

Вариант второй:

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

На самом деле, эти две программы, каждая имеет свои преимущества и недостатки.

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

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

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

При вызове параллельно, принимая во внимание необходимость «технологии пула», который не может быть неограниченным создавать слишком много потоков на процесс JVM. В то же время, также принимая во внимание содержание данных между пластиной и пластиной, в соответствии с порядком на дизайн продукта, чтобы сделать вес.

Согласно этому сценарию спроса, мы выбираем второй вариант для достижения более подходящими.

Вариант II выбран, мы абстрактная упрощенная модель, показанные ниже:

Простая модель

Т1, Т2, Т3 представляет собой множество блоков контента нитей. Результаты возвращаются первым поток T1, Т2 нить возвращает результаты не могут быть повторены, и результат содержания, возвращаемого нить Т1, Т3 нить возвращает результаты не могут, Т2 нить возвращает два результата является излишним Т1.

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

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

3, Future Design Patterns боя


Будущий дизайн интерфейса:

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

Будущий метод интерфейса обеспечивает:
`` `
// Получить результаты задачи
V получает () бросает InterruptedException, ExecutionException ;

// Получить поддержку тайм - аут Результаты задачи
V GET (Long тайм - аут, TimeUnit Unit)
бросает InterruptedException, ExecutionException, TimeoutException;

// определить, является ли задача завершена
булево IsDone ();

// определить, является ли задача была отменена
булево isCancelled ();

// отменить задание
булево ОТМЕНА (булево mayInterruptIfRunning);
`` `

Как правило, мы принимаем во внимание при использовании в будущем получить результаты выполнения задач будут использоваться для реализации FutureTask ThreadPoolExecutor или функциональных требований.

ThreadPoolExecutor, FutureTask фиг класс отношений и интерфейс будущего:

диаграмма класса дизайна будущего

TheadPoolExecutor представить предлагает три метода:

// 1. 提交无需返回值的任务,Runnable 接口 run() 方法无返回值
public Future<?> submit(Runnable task) {
}

// 2. 提交需要返回值的任务,Callable 接口 call() 方法有返回值
public <T> Future<T> submit(Callable<T> task) {
}

// 3. 提交需要返回值的任务,任务结果是第二个参数 result 对象
public <T> Future<T> submit(Runnable task, T result) {
}

Разместить третий примерный способ заключается в следующем:

static String x = "东升的思考";
public static void main(String[] args) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(1);
    // 创建 Result 对象 r
    Result r = new Result();
    r.setName(x);

    // 提交任务
    Future<Result> future =
                    executor.submit(new Task(r), r);
    Result fr = future.get();

    // 下面等式成立
    System.out.println(fr == r);
    System.out.println(fr.getName() == x);
    System.out.println(fr.getNick() == x);
}

static class Result {
    private String name;
    private String nick;
    // ... ignore getter and setter 
}

static class Task implements Runnable {
    Result r;

    // 通过构造函数传入 result
    Task(Result r) {
            this.r = r;
    }

    @Override
    public void run() {
            // 可以操作 result
            String name = r.getName();
            r.setNick(name);
    }
}

Результаты справедливы.

дизайн FutureTask и реализация:

Реализует Runnable и будущие два интерфейса. Реализует интерфейс Runnable, это можно объяснить как объект задачи, представленный непосредственно ThreadPoolExecutor для выполнения. Будущие реализует интерфейс, инструкции могут быть получены для выполнения задачи возвращают результаты.

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

public static void main(String[] args) throws Exception {
    // 创建任务 T1 的 FutureTask,调用推荐接口获取数据
    FutureTask<String> ft1 = new FutureTask<>(new T1Task());
    // 创建任务 T1 的 FutureTask,调用搜索接口获取数据,依赖 T1 结果
    FutureTask<String> ft2  = new FutureTask<>(new T2Task(ft1));
    // 线程 T1 执行任务 ft1
    Thread T1 = new Thread(ft1);
    T1.start();
    // 线程 T2 执行任务 ft2
    Thread T2 = new Thread(ft2);
    T2.start();
    // 等待线程 T2 执行结果
    System.out.println(ft2.get());
}

// T1Task 调用推荐接口获取数据
static class T1Task implements Callable<String> {
    @Override
    public String call() throws Exception {
            System.out.println("T1: 调用推荐接口获取数据...");
            TimeUnit.SECONDS.sleep(1);

            System.out.println("T1: 得到推荐接口数据...");
            TimeUnit.SECONDS.sleep(10);
            return " [T1 板块数据] ";
    }
}
        
// T2Task 调用搜索接口数据,同时需要推荐接口数据
static class T2Task implements Callable<String> {
    FutureTask<String> ft1;

    // T2 任务需要 T1 任务的 FutureTask 返回结果去重
    T2Task(FutureTask<String> ft1) {
         this.ft1 = ft1;
    }

    @Override
    public String call() throws Exception {
        System.out.println("T2: 调用搜索接口获取数据...");
        TimeUnit.SECONDS.sleep(1);

        System.out.println("T2: 得到搜索接口的数据...");
        TimeUnit.SECONDS.sleep(5);
        // 获取 T2 线程的数据
        System.out.println("T2: 调用 T1.get() 接口获取推荐数据");
        String tf1 = ft1.get();
        System.out.println("T2: 获取到推荐接口数据:" + tf1);

        System.out.println("T2: 将 T1 与 T2 板块数据做去重处理");
        return "[T1 和 T2 板块数据聚合结果]";
    }
}

Результаты исполнения заключаются в следующем:

> Task :FutureTaskTest.main()
T1: 调用推荐接口获取数据...
T2: 调用搜索接口获取数据...
T1: 得到推荐接口数据...
T2: 得到搜索接口的数据...
T2: 调用 T1.get() 接口获取推荐数据
T2: 获取到推荐接口数据: [T1 板块数据] 
T2: 将 T1 与 T2 板块数据做去重处理
[T1 和 T2 板块数据聚合结果] 

Краткое описание:

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

4, CompleteableFuture реальный режим


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

Эта проблема существует уже в JDK1.5 начале проектирования, разработки JDK1.8 представил CompletableFuture, прежде чем они получат идеальный аксессуар.

В то же время, с открытым исходным кодом инструментарий Google предоставляет Guava ListenableFuture, поддерживать обратные вызовы, заинтересованные друзья могут получить доступ к своей собственной поддержки исследований, когда задача выполнена.

Во введении к сценарию бизнеса спроса, различные секции источников данных различны, и что есть зависимость данных между пластиной и пластиной.

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

CompletableFuture 类 图:

CompletableFuture 类 图

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

CompletableFuture основные достоинства:

1) без ручного обслуживания нити, нить назначены задачи без необходимости разработчиков сосредоточиться на;

2) При использовании, семантика более ясным;

Например: t3 = t1.thenCombine (t2, () -> {// ...} йоЗотеЬЫпд может быть четко указано после выполнения задачи 3 и задачи ждать завершения задания 2 начнется.

3) код более кратким, чтобы поддерживать сцепленные вызовы, что позволяет сосредоточиться на бизнес-логике.

4) легко обработки исключений

Далее, для имитации многодисковой обработки данных агрегации CompletableFuture в альбоме.

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

public static void main(String[] args) throws Exception {
    // 暂存数据
    List<String> stashList = Lists.newArrayList();
    // 任务 1:调用推荐接口获取数据
    CompletableFuture<String> t1 =
                    CompletableFuture.supplyAsync(() -> {
                            System.out.println("T1: 获取推荐接口数据...");
                            sleepSeconds(5);
                            stashList.add("[T1 板块数据]");
                            return "[T1 板块数据]";
                    });
    // 任务 2:调用搜索接口获取数据
    CompletableFuture<String> t2 =
                    CompletableFuture.supplyAsync(() -> {
                            System.out.println("T2: 调用搜索接口获取数据...");
                            sleepSeconds(3);
                            return " [T2 板块数据] ";
                    });
    // 任务 3:任务 1 和任务 2 完成后执行,聚合结果
    CompletableFuture<String> t3 =
                    t1.thenCombine(t2, (t1Result, t2Result) -> {
                            System.out.println(t1Result + " 与 " + t2Result + "实现去重逻辑处理");
                            return "[T1 和 T2 板块数据聚合结果]";
                    });
    // 等待任务 3 执行结果
    System.out.println(t3.get(6, TimeUnit.SECONDS));
}

static void sleepSeconds(int timeout) {
    try {
            TimeUnit.SECONDS.sleep(timeout);
    } catch (InterruptedException e) {
            e.printStackTrace();
    }
}

Результаты исполнения заключаются в следующем:

> Task :CompletableFutureTest.main()
T1: 获取推荐接口数据...
T2: 调用搜索接口获取数据...
[T1 板块数据] 与  [T2 板块数据] 实现去重逻辑处理
[T1 和 T2 板块数据聚合结果]

В примере кода выше IDEA в новом классе, копируется непосредственно в, чтобы работать должным образом.

** 5, производство предложение CompletableFuture **


Создать разумный пул потоков:

В производственной среде, без непосредственного использования рекомендуемого образца кода. Поскольку образец кода , используемого в
CompletableFuture.supplyAsync(() -> {});
supplyAsync () метод (здесь используется в шаблоне Фабричный метод) для создания объектов CompletableFuture, основной пул потоков , используемый по умолчанию, не в состоянии удовлетворить потребности бизнеса.

Связывание взгляд, лежащий в основе источника:

// 默认使用 ForkJoinPool 线程池
private static final Executor asyncPool = useCommonPool ?
       ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
     return asyncSupplyStage(asyncPool, supplier);
}

Создание ForkJoinPool пула потоков:
по умолчанию размера пула потоков является Runtime.getRuntime () availableProcessors () - 1 (CPU основного номера --1), вы можете установить размер пула потоков с помощью параметров -Djava.util.concurrent.ForkJoinPool.common.parallelism виртуальной машины Java.

-Djava.util.concurrent.ForkJoinPool.common.threadFactory параметры конфигурации на основе нити заводских параметров виртуальной машины Java; -Djava.util.concurrent.ForkJoinPool.common.exceptionHandler параметры конфигурации класс исключения из этих двух параметров, установленных внутри, через систему загрузчик классов загружает класс.

Если все CompletableFuture использовать пул потоков по умолчанию, когда выполнение задачи является операции медленно ввода / вывода, это приведет все потоки блокируются на операции ввода / вывода, тем самым влияя на общую производительность системы.

Поэтому, мы рекомендуем использовать в производственной среде, 根据不同的业务类型创建不同的线程池,以避免互相影响.

CompletableFuture также обеспечивает другой способ поддержки пула потоков.

// 第二个参数支持传递 Executor 自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor) {
        return asyncSupplyStage(screenExecutor(executor), supplier);
}

Пользовательский пул потоков, предлагаемый справочник «Алибаба Java Development Manual», рекомендуется ThreadPoolExecutor пользовательского пула потоков, использовать ограниченные очереди, размер очереди устанавливаются в соответствии с реальной бизнес-ситуациями.

настройки размера пула потоков в «Java Параллелизм реальной» книги, Брайан Гетц предоставил много рекомендаций по оптимизации. Если чрезмерное количество пула потоков, конкурентоспособны ресурсов процессора и памяти, в результате чего много времени на переключение контекста. И наоборот, если число потоков пула слишком мал, вы не можете в полной мере использовать преимущества многоядерных процессоров.

Коэффициент использования размера процессор ЦП пула потоков может быть оценен с помощью следующего уравнения:

размер пула резьбы рассчитывается

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

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

// 类似于 try{}catch{} 中的 catch{}
public CompletionStage<T> exceptionally
        (Function<Throwable, ? extends T> fn);
                
// 类似于 try{}finally{} 中的 finally{},不支持返回结果
public CompletionStage<T> whenComplete
        (BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync
        (BiConsumer<? super T, ? super Throwable> action);
                
// 类似于 try{}finally{} 中的 finally{},支持返回结果
public <U> CompletionStage<U> handle
        (BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync
        (BiFunction<? super T, Throwable, ? extends U> fn);

#### 6, тест производительности CompletableFuture:

Давление цикл измерения , как показано количество задач, каждый раз , когда измеренное давление, результаты суперпозиции сходимости к jobNum данных, отнимающие много времени расчетов.
Статистика размеры: CompletableFuture по умолчанию и пользовательский пул потоков пула потоков.
Производительность Код проверки:

// 性能测试代码
Arrays.asList(-3, -1, 0, 1, 2, 4, 5, 10, 16, 17, 30, 50, 100, 150, 200, 300).forEach(offset -> {
                    int jobNum = PROCESSORS + offset;
                    System.out.println(
                                    String.format("When %s tasks => stream: %s, parallelStream: %s, future default: %s, future custom: %s",
                                                    testCompletableFutureDefaultExecutor(jobNum), testCompletableFutureCustomExecutor(jobNum)));
});

// CompletableFuture 使用默认 ForkJoinPool 线程池
private static long testCompletableFutureDefaultExecutor(int jobCount) {
    List<CompletableFuture<Integer>> tasks = new ArrayList<>();
    IntStream.rangeClosed(1, jobCount).forEach(value -> tasks.add(CompletableFuture.supplyAsync(CompleteableFuturePerfTest::getJob)));

    long start = System.currentTimeMillis();
    int sum = tasks.stream().map(CompletableFuture::join).mapToInt(Integer::intValue).sum();
    checkSum(sum, jobCount);
    return System.currentTimeMillis() - start;
}

// CompletableFuture 使用自定义的线程池
private static long testCompletableFutureCustomExecutor(int jobCount) {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(200, 200, 5, TimeUnit.MINUTES, new ArrayBlockingQueue<>(100000), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("CUSTOM_DAEMON_COMPLETABLEFUTURE");
                    thread.setDaemon(true);
                    return thread;
            }
    }, new ThreadPoolExecutor.CallerRunsPolicy());

    List<CompletableFuture<Integer>> tasks = new ArrayList<>();
    IntStream.rangeClosed(1, jobCount).forEach(value -> tasks.add(CompletableFuture.supplyAsync(CompleteableFuturePerfTest::getJob, threadPoolExecutor)));

    long start = System.currentTimeMillis();
    int sum = tasks.stream().map(CompletableFuture::join).mapToInt(Integer::intValue).sum();
    checkSum(sum, jobCount);
    return System.currentTimeMillis() - start;
}

Тест конфигурация машины: 8-ядерный процессор, память 16G

Результаты тестов производительности:

Результаты тестов производительности

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

7, CompletableFuture Extended:


Создание объекта:

В дополнение к вышеупомянутым способам supplyAsync, CompletableFuture также предоставляет следующие методы:

// 执行任务,CompletableFuture<Void> 无返回值,默认线程池
public static CompletableFuture<Void> runAsync(Runnable runnable) {
      return asyncRunStage(asyncPool, runnable);
}
// 执行任务,CompletableFuture<Void> 无返回值,支持自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
}

Мы CompletableFuture боевой режим, называемый в CompletableFuture реализуется интерфейс CompletionStage, который обеспечивает очень широкие функциональные возможности.

Интерфейс CompletionStage поддерживает последовательные отношения, объединение и отношения, объединение или отношения.
Следующие интерфейсы для этих отношений , чтобы быть простым описанием, вы можете пойти , чтобы получить доступ непосредственно JDK API при использовании.
Между тем, каждый из этих отношений интерфейсов обеспечивает способ соответствующего метода xxxAsync () для асинхронного выполнения задач.

Последовательная связь:

Описание CompletionStage Серийные отношения, в основном thenApply, thenRun, thenAccept и последовательный интерфейс thenCompose.

Источник следующим образом:

// 对应 U apply(T t) ,接收参数 T并支持返回值 U
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);

// 不接收参数也不支持返回值
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);

// 接收参数但不支持返回值
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);

// 组合两个依赖的 CompletableFuture 对象
public <U> CompletionStage<U> thenCompose
        (Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync
        (Function<? super T, ? extends CompletionStage<U>> fn);

Агрегирование и отношения:

CompletionStage описывают сходимость и отношения, есть thenCombine, thenAcceptBoth и runAfterBoth последовательный интерфейс.

Источник ниже (не метод Асинхронный):

// 当前和另外的 CompletableFuture 都完成时,两个参数传递给 fn,fn 有返回值
public <U,V> CompletionStage<V> thenCombine
        (CompletionStage<? extends U> other,
         BiFunction<? super T,? super U,? extends V> fn);

// 当前和另外的 CompletableFuture 都完成时,两个参数传递给 action,action 没有返回值
public <U> CompletionStage<Void> thenAcceptBoth
        (CompletionStage<? extends U> other,
         BiConsumer<? super T, ? super U> action);

// 当前和另外的 CompletableFuture 都完成时,执行 action
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,
                                              Runnable action);

Агрегация или отношения:

CompletionStage описывают сходимость или отношения, есть applyToEither, acceptEither и runAfterEither последовательный интерфейс.

Источник ниже (не метод Асинхронный):

// 当前与另外的 CompletableFuture 任何一个执行完成,将其传递给 fn,支持返回值
public <U> CompletionStage<U> applyToEither
        (CompletionStage<? extends T> other,
         Function<? super T, U> fn);

// 当前与另外的 CompletableFuture 任何一个执行完成,将其传递给 action,不支持返回值
public CompletionStage<Void> acceptEither
        (CompletionStage<? extends T> other,
         Consumer<? super T> action);

// 当前与另外的 CompletableFuture 任何一个执行完成,直接执行 action
public CompletionStage<Void> runAfterEither(CompletionStage<?> other,
                                                Runnable action);

Это, связанные с CompletableFuture функции введены более.

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

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

Я хочу, чтобы помочь!

Я приветствую внимание общественности числа, сканировать двумерный код, чтобы разблокировать внимание более интересные статьи, расти вместе с вами ~
Java энтузиастов сообщество

рекомендация

отwww.cnblogs.com/ldws/p/11627139.html