CompletableFuture - уже здесь

CompletableFuture
уже здесь
Дмитрий Чуйко
dmitry.chuyko@oracle.com
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
Содержание
Введение
API
Накладные расходы
Пример. Rest-сервис
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
2/39
Safe Harbor Statement
The following is intended to outline our general product direction. It
is intended for information purposes only, and may not be
incorporated into any contract. It is not a commitment to deliver any
material, code, or functionality, and should not be relied upon in
making purchasing decisions. The development, release, and timing
of any features or functionality described for Oracle’s products
remains at the sole discretion of Oracle.
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
3/39
Введение
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
4/39
Параллелизм в Java: История
∙
Threads
– Monitors
– Locks and other primitives
∙
Executors
– Runnable<T>, Callable<T> → lambdas
∙
∙
∙
∙
Future<T>
ForkJoinPool
stream.parallel()
Explicit → implicit
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
5/39
Параллелизм в Java: Future<T>
∙
∙
Обёртка для результата, доступного в будущем
Получение значения требует проверки исключений
– ExecutionException содержит исходное unchecked исключение
∙
∙
∙
Исполняется в каком-то потоке и блокирует какой-то
(другой) поток
Можно проверить, доступно ли уже значение
Можно отменить вычисление
Future f = executor . submit (() -> ...);
result = f . get ();
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
6/39
Сложности: Обработка ошибок
try {
try {
return parse ( fileObject , content . get ());
} catch ( E x e c u t i o n E x c e p t i o n e ) {
unfold ( e );
}
} catch ( IOException e ) {
log . error (e , fileObject );
} catch ( I n t e r r u p t e d E x c e p t i o n e ) {
throw new R u n t i m e E x c e p t i o n ( e );
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
7/39
Сложности: Обработка ошибок
private static void unfold ( E x e c u t i o n E x c e p t i o n e ) throws IOException ,
InterruptedException {
Throwable cause = e . getCause ();
if ( cause instanceof E x e c u t i o n E x c e p t i o n )
unfold (( E x e c u t i o n E x c e p t i o n ) cause );
if ( cause instanceof IOException )
throw ( IOException ) cause ;
if ( cause instanceof I n t e r r u p t e d E x c e p t i o n )
throw ( I n t e r r u p t e d E x c e p t i o n ) cause ;
if ( cause instanceof Error )
throw ( Error ) cause ;
if ( cause instanceof R u n t i m e E x c e p t i o n )
throw ( R u n t i m e E x c e p t i o n ) cause ;
throw new R u n t i m e E x c e p t i o n ( e );
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
8/39
Сложности: Давайте добавим обработчики!
readFileAsync ( file ,
content -> System . out . println ( content );
ex -> ex . p ri n tS ta c kT r ac e ();
);
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
9/39
Сложности: Давайте добавим обработчики!
readFileAsync ( file ,
content -> System . out . println ( content );
ex -> ex . p ri n tS ta c kT r ac e ();
);
∙
↓Добро пожаловать сюда↓
http://callbackhell.com/
readFileAsync ( file ,
content -> p r o c e s s C o n t e n t A s y n c ( content ,
c -> System . out . println ( c ) ,
e -> e . p r in tS t ac kT r ac e ()
);
ex -> ex . p ri n tS ta c kT r ac e ();
);
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
9/39
CompletableFuture: Боремся с вложенностью
C o m p l e ta b l e F u t u r e . supplyAsync (() -> readFile ( file ))
. t he n C o m p o s e A s y n c ( content -> p rocess Conten t ( content ))
. whenComplete (( result , ex ) -> {
if ( ex == null ) {
System . out . println ( result );
} else {
ex . pr in t St ac k Tr ac e ();
}
});
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
10/39
CompletableFuture: Композиция
∙
Method chaining
– Нет блокирующих вызовов
– Знакомо и удобно (builders, mocks, streams)
∙
Работаем с несколькими CF
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
11/39
API
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
12/39
Что за класс: CompletableFuture<T>
∙
∙
∙
Core library class since Java SE 8
implements Future<T>
implements CompletionStage<T>
–
–
–
–
–
Элемент композиции
Вычисление функции
Связь через результаты
Методы преобразований
toCompletableFuture()
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
13/39
Методы: Создание
∙
CompletableFuture()
– boolean complete(T value), успешно только для одного потока
∙
∙
∙
∙
static <U> CompletableFuture<U> completedFuture(U value)
static <U> CompletableFuture<U>
supplyAsync(Supplier<U> supplier[, Executor executor])
static CompletableFuture<Void>
runAsync(Runnable runnable[, Executor executor])
ForkJoinPool.commonPool() по умолчанию
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
14/39
Методы: Создание
CompletableFuture < Long > cf1 = C o m p l e t a b l e F u t u r e . supplyAsync (() -> 42 L );
CompletableFuture < Long > start = C o m p l e t a b l e F u t u r e . c o mp le t ed Fu t ur e (42 L );
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
15/39
Методы: *Отступление
Упрощённая запись generics
∙
∙
Function<? super T,? extends U>
→ Function<T,U>
Consumer<? super T> → Consumer<T>
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
16/39
Методы: Трансформации
∙
<U> CompletableFuture<U> thenApply(Function<T,U> fn)
– Нет "Async"→ продолжаем в том же потоке
∙
<U> CompletableFuture<U>
thenApplyAsync(Function<T,U> fn [, Executor executor])
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
17/39
Методы: Трансформации (map)
CompletableFuture < Long > cf2 = C o m p l e t a b l e F u t u r e
. supplyAsync (() -> 42 L )
. thenApply ( r1 -> r1 + 2015);
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
18/39
Методы: Подписка
∙
∙
∙
CompletableFuture<Void> thenAccept(Consumer<T> block);
CompletableFuture<Void> thenRun(Runnable action);
Async versions
CompletableFuture < Void > cf3 = C o m p l e t a b l e F u t u r e
. supplyAsync (() -> 42 L )
. thenApply ( r1 -> r1 + 2015)
. thenAccept ( System . out :: println );
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
19/39
Методы: Обработка ошибок
∙
∙
∙
CompletableFuture<T>
exceptionally(Function<Throwable, T> fn)
CompletableFuture<U>
handle(BiFunction<T, Throwable, U> fn)
Ошибка пробрасывается по цепочкам
C o m p l e ta b l e F u t u r e . supplyAsync (() -> readFile ( file ))
. t he n C o m p o s e A s y n c ( content -> p rocess Conten t ( content ))
. thenAccept ( System . out :: println )
. exceptionally ( Throwable :: p ri nt S ta ck T ra ce );
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
20/39
Методы: Комбинация (reduce)
∙
<U,V> CompletableFuture<V> thenCombine
(CompletionStage<U> other, BiFunction<T,U,V> fn)
CompletableFuture < Long > cf2 = C o m p l e t a b l e F u t u r e
. supplyAsync (() -> 42 L )
. thenCombine ( C o m p l e t a b l e F u t u r e . supplyAsync (() -> 2015) , Math :: min );
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
21/39
Методы: Для набора
∙
∙
CompletableFuture<Object>
anyOf(CompletableFuture<?>... cfs)
CompletableFuture<Void>
allOf(CompletableFuture<?>... cfs)
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
22/39
Методы: Композиция (flatMap)
∙
CompletableFuture<U>
thenCompose(Function<T,CompletableFuture<U>> fn)
CompletableFuture < Long > cff = C o m p l e t a b l e F u t u r e
. supplyAsync (() -> 42 L )
. thenCompose ( x -> C o m p l e t a b l e F u t u r e . supplyAsync (() -> x + 2015));
CompletableFuture < CompletableFuture < Long > > cff = C o m p l e t a b l e F u t u r e
. supplyAsync (() -> 42 L )
. thenApply ( x -> C o m p l e t a b l e F u t u r e . supplyAsync (() -> x + 2015));
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
23/39
Методы: Get
∙
∙
∙
∙
T get() throws InterruptedException, ExecutionException
T get(long timeout, TimeUnit unit) throws
InterruptedException, ExecutionException, TimeoutException
T getNow(T valueIfAbsent)
T join()
– Нет checked exceptions (CompletionException)
...
stream . map ( x -> C o m p l e t a b l e F u t u r e ...). map ( C o m p l e t a b l e F u t u r e :: join )
...
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
24/39
Накладные расходы
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
25/39
Бенчмарк: Окружение
∙
∙
∙
∙
Intel Core i5-3320M (1x2x2, 3.3 GHz)
Linux x64 (kernel 3.16)
JDK 8u40
OpenJDK JMH 1.8
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
26/39
Бенчмарк: Базовые операции
@Param ({ " 1024 " })
public volatile int loada ;
@Param ({ " 1024 " })
public volatile int loadb ;
Integer a () {
Blackhole . consumeCPU ( loada );
return loada ;
}
Integer b () {
Blackhole . consumeCPU ( loadb );
return loadb ;
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
27/39
Бенчмарк: Что сравниваем
Простой вариант
@Benchmark
public Integer ab () {
return a () * b ();
}
@Benchmark
public Integer stream () throws InterruptedException , E x e c u t i o n E x c e p t i o n {
return IntStream . range (0 , 2)
. mapToObj (( i ) -> i == 0 ? a () : b ())
. reduce (1 , (a , b ) -> a * b );
}
@Benchmark
public Integer parstream () throws InterruptedException , E x e c u t i o n E x c e p t i o n {
return IntStream . range (0 , 2). parallel ()
. mapToObj (( i ) -> i == 0 ? a () : b ())
. reduce (1 , (a , b ) -> a * b );
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
28/39
Бенчмарк: Что сравниваем
Future
@Benchmark
public Integer future2 () throws InterruptedException , E x e c u t i o n E x c e p t i o n {
E xe cu t or S er vi c e fjp = ForkJoinPool . commonPool ();
Future < Integer > fa = fjp . submit (() -> a ());
Future < Integer > fb = fjp . submit (() -> b ());
return fa . get () * fb . get ();
}
@Benchmark
public Integer future3 () throws InterruptedException , E x e c u t i o n E x c e p t i o n {
E xe cu t or S er vi c e fjp = ForkJoinPool . commonPool ();
Future < Integer > fa = fjp . submit (() -> a ());
Future < Integer > fb = fjp . submit (() -> b ());
return fjp . submit (() -> fa . get () * fb . get ()). get ();
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
29/39
Бенчмарк: Что сравниваем
CompletableFuture
@Benchmark
public Integer cf2 () throws InterruptedException , E x e c u t i o n E x c e p t i o n {
CompletableFuture < Integer > cfa = C o m p l e t a b l e F u t u r e . supplyAsync (() -> a ());
CompletableFuture < Integer > cfb = C o m p l e t a b l e F u t u r e . supplyAsync (() -> b ());
return cfa . get () * cfb . get ();
}
@Benchmark
public Integer cf3 () throws InterruptedException , E x e c u t i o n E x c e p t i o n {
return C o m p l e t a b l e F u t u r e . supplyAsync (() -> a ())
. thenCombine ( C o m p l e t a b l e F u t u r e
. supplyAsync (() -> b ()) , (a , b ) -> a * b ). get ();
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
30/39
Бенчмарк: Результаты
Последовательная обработка лучше (loada=loadb=1024)
Вариант
𝜇c/оп
ab 4.7 ± 0.1
cf2 7.9 ± 0.8
cf3 11.4 ± 0.7
future2 12.5 ± 0.4
future3 13.3 ± 0.7
parstream 9.9 ± 0.5
stream 4.9 ± 0.1
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
31/39
Бенчмарк: Результаты
Оценка пустой операции (loada=loadb=0)
Вариант
ab
cf2
cf3
future2
future3
parstream
stream
нc/оп
5.0 ± 0.7
703.3 ± 285.9
1647.2 ± 523.2
6315.4 ± 632.8
7591.9 ± 1032.3
1234.4 ± 202.8
74.8 ± 5.5
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
32/39
Бенчмарк: Результаты
Параллельная обработка лучше (loada=loadb=131070)
Вариант
ab
cf2
cf3
future2
future3
parstream
stream
𝜇c/оп
596.5 ± 2.3
360.0 ± 17.3
342.6 ± 13.5
359.6 ± 7.9
342.5 ± 9.8
334.8 ± 18.4
597.1 ± 2.8
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
33/39
Пример. Rest-сервис
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
34/39
Сервис: Ингридиенты
∙
∙
JDK 8u40
JAX-RS 2.0
https://jax-rs-spec.java.net/
∙
Jersey RI
https://jersey.java.net/
∙
Grizzly NIO framework
https://grizzly.java.net/
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
35/39
Сервис: Ингридиенты
∙
∙
JDK 8u40
JAX-RS 2.0
https://jax-rs-spec.java.net/
∙
Jersey RI
https://jersey.java.net/
∙
Grizzly NIO framework
https://grizzly.java.net/
∙
CompletableFuture
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
35/39
Сервис: Запускалка
public class Main {
public static final String BASE_URI = " http :// localhost :8080/ jpoint / " ;
public static void main ( String [] args ) throws IOException {
Res ourceC onfig rc = new R esourc eConfi g (). packages ( " com . oracle . demo " );
HttpServer server = G r i z z l y H t t p S e r v e r F a c t o r y
. c r e a t e H t t p S e r v e r ( URI . create ( BASE_URI ) , rc );
System . out . println ( String . format ( " Jersey ␣ app ␣ started ␣ "
+ " with ␣ WADL ␣ available ␣ at ␣ % sapplication . wadl \ n "
+ " Hit ␣ enter ␣ to ␣ stop ␣ it ... " , BASE_URI ));
System . in . read ();
server . shutdownNow ();
}
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
36/39
Сервис: Endpoint
@Path ( " jpoint " )
public class JPoint {
@Inject
DataService dataService ;
@GET
@Produces ( MediaType . TEXT_PLAIN )
public void asyncGet ( @Suspended final AsyncResponse asyncResponse ) {
dataService
. findAudience ()
. thenCombine ( dataService . fi ndImpr ession () , (a , b ) -> a + b )
. thenApply ( asyncResponse :: resume )
. exceptionally ( asyncResponse :: resume );
// way #1
asyncResponse . setTimeout (1 , SECONDS );
asyncResponse . s e t T i m e o u t H a n d l e r ( ar -> ar . resume ( new T i m e o u t E x c e p t i o n ()));
}
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
37/39
Сервис: Provider
Часть 1/2
@ManagedBean
@Path ( " data " )
public class DataService {
// @Inject
S c h e d u l e d E x e c u t o r S e r v i c e shedPool = Executors . n e w S c h e d u l e d T h r e a d P o o l (1);
public CompletableFuture < String > findAudience () {
return find ( " audience " );
}
public CompletableFuture < String > findIm pressi on () {
return find ( " impression " );
}
@PreDestroy
public void shutdown () {
shedPool . shutdownNow ();
}
...
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
38/39
Сервис: Provider
Часть 2/2
public CompletableFuture < String > find ( String path ) {
CompletableFuture < String > promise = new CompletableFuture < >();
CompletableFuture
. runAsync (() -> {
try {
promise . complete ( new String ( Files . readAllBytes ( Paths
. get ( path ))));
} catch ( IOException e ) {
promise . c o m p l e t e E x c e p t i o n a l l y ( e );
}
});
// way #2
shedPool . schedule (
() -> promise . c o m p l e t e E x c e p t i o n a l l y ( new T i m e o u t E x c e p t i o n ()) , 1 ,
SECONDS );
return promise ;
}
}
c 2015, Oracle and/or its affiliates. All rights reserved.
Copyright ○
39/39