Удосконалення Java 9 CompletableFuture API

1. Вступ

Java 9 має деякі зміни в класі CompletableFuture . Такі зміни були введені як частина JEP 266 з метою розгляду поширених скарг та пропозицій з моменту його введення в JDK 8, точніше, підтримки затримок та тайм-аутів, кращої підтримки підкласифікації та декількох корисних методів.

З точки зору коду, API постачається з вісьмома новими методами та п’ятьма новими статичними методами. Щоб увімкнути такі доповнення, було змінено приблизно 1500 з 2400 рядків коду (відповідно до Open JDK).

2. Додавання API екземпляра

Як вже зазначалося, API екземпляра постачається з вісьмома новими доповненнями:

  1. Виконавець за замовчуванням Виконавець ()
  2. CompletableFuture newIncompleteFuture ()
  3. Повна версія майбутньої копії ()
  4. CompletionStage minimalCompletionStage ()
  5. CompletableFuture completeAsync (постачальник постачальника, виконавець виконавця)
  6. CompletableFuture completeAsync (постачальник постачальника)
  7. CompletableFuture абоTimeout (довгий тайм-аут, одиниця виміру часу)
  8. CompletableFuture completeOnTimeout (значення T, довгий час очікування, одиниця виміру TimeUnit)

2.1. Метод defaultExecutor ()

Підпис : Виконавець за замовчуванням Виконавець ()

Повертає виконавець за замовчуванням, що використовується для асинхронних методів, які не визначають виконавця .

new CompletableFuture().defaultExecutor()

Це можна замінити підкласами, що повертають виконавця, що забезпечує, принаймні, один незалежний потік.

2.2. Метод newIncompleteFuture ()

Підпис : CompletableFuture newIncompleteFuture ()

NewIncompleteFuture , також відомий як «віртуальний конструктор», використовується , щоб отримати новий екземпляр completable майбутнє одного і того ж типу.

new CompletableFuture().newIncompleteFuture()

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

2.3. Копія методу ()

Підпис : Заповнювана майбутня копія ()

Цей метод повертає новий CompletableFuture, який:

  • Коли це завершується нормально, нове також завершується нормально
  • Коли це виконується виключно за винятком X, нове також заповнюється виключно з CompletionException із X як причиною
new CompletableFuture().copy()

Цей метод може бути корисним як форма "захисного копіювання", щоб запобігти виконанню клієнтами, і в той же час мати можливість організувати залежні дії щодо конкретного екземпляра CompletableFuture .

2.4. Метод minimalCompletionStage ()

Підпис : CompletionStage minimalCompletionStage ()

Цей метод повертає новий CompletionStage, який поводиться точно так само, як описано методом копіювання, однак такий новий екземпляр кидає UnsupportedOperationException при кожній спробі отримати або встановити вирішене значення.

new CompletableFuture().minimalCompletionStage()

Новий CompletableFuture з усіма доступними методами можна отримати за допомогою методу toCompletableFuture, доступного в API CompletionStage .

2.5. Методи completeAsync ()

Метод completeAsync слід використовувати для завершення CompletableFuture асинхронно, використовуючи значення, надане Постачальником .

Підписи :

CompletableFuture completeAsync(Supplier supplier, Executor executor) CompletableFuture completeAsync(Supplier supplier)

Різниця між цими двома перевантаженими методами полягає у існуванні другого аргументу, де може бути вказаний Виконавець, що виконує завдання. Якщо жодного не вказано, буде використаний виконавець за замовчуванням (повернений методом defaultExecutor ).

2.6. Methods orTimeout()

Signature: CompletableFuture orTimeout(long timeout, TimeUnit unit)

new CompletableFuture().orTimeout(1, TimeUnit.SECONDS)

Resolves the CompletableFuture exceptionally with TimeoutException, unless it is completed before the specified timeout.

2.7. Method completeOnTimeout()

Signature: CompletableFuture completeOnTimeout(T value, long timeout, TimeUnit unit)

new CompletableFuture().completeOnTimeout(value, 1, TimeUnit.SECONDS)

Completes the CompletableFuture normally with the specified value unless it is completed before the specified timeout.

3. Static API Additions

Some utility methods were also added. They are:

  1. Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
  2. Executor delayedExecutor(long delay, TimeUnit unit)
  3. CompletionStage completedStage(U value)
  4. CompletionStage failedStage(Throwable ex)
  5. CompletableFuture failedFuture(Throwable ex)

3.1. Methods delayedExecutor

Signatures:

Executor delayedExecutor(long delay, TimeUnit unit, Executor executor) Executor delayedExecutor(long delay, TimeUnit unit)

Returns a new Executor that submits a task to the given base executor after the given delay (or no delay if non-positive). Each delay commences upon invocation of the returned executor's execute method. If no executor is specified the default executor (ForkJoinPool.commonPool()) will be used.

3.2. Methods completedStage and failedStage

Signatures:

 CompletionStage completedStage(U value)  CompletionStage failedStage(Throwable ex)

This utility methods return already resolved CompletionStage instances, either completed normally with a value (completedStage) or completed exceptionally (failedStage) with the given exception.

3.3. Method failedFuture

Signature: CompletableFuture failedFuture(Throwable ex)

The failedFuture method adds the ability to specify an already completed exceptionally CompleatebleFuture instance.

4. Example Use Cases

Within this section, one will show some examples on how to use some of the new API.

4.1. Delay

This example will show how to delay the completion of a CompletableFuture with a specific value by one second. That can be achieved by using the completeAsync method together with the delayedExecutor.

CompletableFuture future = new CompletableFuture(); future.completeAsync(() -> input, CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS));

4.2. Complete With Value on Timeout

Another way to achieve a delayed result is to use the completeOnTimeout method. This example defines a CompletableFuture that will be resolved with a given input if it stays unresolved after 1 second.

CompletableFuture future = new CompletableFuture(); future.completeOnTimeout(input, 1, TimeUnit.SECONDS);

4.3. Timeout

Another possibility is timing out which resolves the future exceptionally with TimeoutException. For example, having the CompletableFuture timing out after 1 second given it is not completed before that.

CompletableFuture future = new CompletableFuture(); future.orTimeout(1, TimeUnit.SECONDS);

5. Conclusion

На закінчення, Java 9 поставляється з декількома доповненнями до API CompletableFuture , тепер вона має кращу підтримку для підкласифікації, завдяки віртуальному конструктору newIncompleteFuture можна взяти під контроль екземпляри CompletionStage, що повертаються в більшості API CompletionStage .

Це, безумовно, має кращу підтримку затримок та тайм-аутів, як показано раніше. Додані методи утиліти дотримуються розумного зразка, надаючи CompletableFuture зручний спосіб вказати вирішені екземпляри.

Приклади, використані в цій статті, можна знайти у нашому сховищі GitHub.