5. Asynchronous Result Processing
1. Overview of concurrency libraries added in
JDK8
2. Asynchronous result processing
3. CompletableFuture (Vs classical Future)
4. StampedLock (Vs ReadWriteLock)
6. Asynchronous Result Processing
– Synchronous
– Not Synchronous, Asynchronous [Java 5]
– Asynchronous and Reactive [Java 8]
Client Server
Request
Server
Proc...
Client Blocked
Response
Synchronous
Client Server
Request
Server
Proc...
Ctrl ret. immed
Asynchronous
Client polls - X
Client polls - Y
Response
Client Server
Request
Server
Proc...
Response Push
Ctrl ret. immed
Asynchronous + Reactive
7. Agenda
1. Overview of concurrency libraries added in
JDK8
2. Asynchronous result processing
3. CompletableFuture (Vs classical Future)
4. StampedLock (Vs ReadWriteLock)
8. Classical Future
• An interface in java.util.concurrent pkg
• FutureTask is the concrete implementation
• Represents the result of an async processing.
• Utility Methods with the interface:
– isDone()
– isCancelled()
– cancel(boolean mayInterruptIfRunning)
• Lets see it in action!
9. Classical Future continued 3
• Future programming model:
ExecutorService pool = ...
Future<Double> bankBalance=pool.submit(()->{
Thread.sleep(5000L);//sim processing time
return bankBalance;
});
try{
double balance = bankBalance.get(); //blocking call
}catch(InterruptedException ignore){}
• Work around:
– May spin around and check with isDone() or isCancelled();
– Use CompletableFuture!
10. CompletableFuture
• A class in java.util.concurrent package
• Implements CompletionStage and Future
interfaces (both in java.util.concurrent pkgs)
• Since CompletableFuture implements Future,
it can be used classically – blockingly!
• CompletionStage is the interface which
provides all the reactive constructs; fat
interface
12. supplyAsync(...) Variants
• publicstatic <U> CompletableFuture<U> supplyAsync(Sup
plier<U> supplier)
– Uses ForkJoinPool.commonPool();
• publicstatic <U> CompletableFuture<U> supplyAsync(Sup
plier<U> supplier, Executor executor)
– Uses the passed into Executor as the thread pool
• supplyAsync’s counterpart – runAsync(...)
– Takes Runnable instead of Supplier
– Returns CompletableFuture<Void>
– Calling get() would return null
13. thenAccept(...) Variants
• public CompletableFuture<Void> thenAccept(Consumer<?
super T> action)
• public CompletableFuture<Void> thenAcceptAsync(Consumer
<? super T> action)
• public CompletableFuture<Void> thenAcceptAsync(Consumer
<? super T> action, Executor executor)
14. More into piping Reactions
• Fluent API helps piping reactions.
• supplyAsync(Supplier)
.whenComplete(BiConsumer)//res, err
.thenApply(Function)
.thenAccept(Consumer)
.thenRun(Runnable)
15. Other Important Piping APIs - 1
• Composition:
public <U> CompletableFuture<U> thenCompose(Function<?
super T,? extends CompletionStage<U>> fn)
(This)
CompletionStage<U>
Function(? super T, ?
extends
CompletionStage<U>)
CompletionStage<T>
16. Other Important Piping APIs - 2
• Combination:
public <U,V> CompletableFuture<V> thenCombine(Completio
nStage<? extends U> other, BiFunction<? super T,? super U,?
extends V> fn)
(This)
CompletionStage
<U>
(Other)
CompletionStage
<V>
BiFunction(?
super T, ? Super
U, ? extends V)
CompletionStage
<T>
17. How we leveraged the power of
CompletableFuture
• Use Case #1 (Remote Serivces)
– Two remote services.
– Both replicate service
– We needed anyone to respond; faster! (acceptEither)
– Lets simulate what we did!
– Can be extended easily to any number of services!
• public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
18. How we leveraged the power of
CompletableFuture - 2
• Use Case #2 (update UI after File FTP completes)
– Application UI uploads file; not on UI thread;
– UI thread is not blocked; it reacts whenever FTP is completed.
– CompletableFuture<FTPStatus> ftp = CompletableFuture.supplyAsync(()->{
return FTPClient.uploadFile(file);
});
ftp . whenComplete((ftpStatus,error)->{ //takes BiConsumer; alt. handle takes
//BiFunction
if (error == null){
//grab UI thread and update of successful FTP
} else{
// grab UI thread and update of failure FTP
}
})
19. Creating APIs to return
CompletableFuture
• How to expose CompletableFuture APIs for Clients!
• Instantiate CompletableFuture
• Set result to it asynchronously.
public CompletableFuture<String> getPageContent(URL url){
CompletableFuture<String> futurePage =
CompletableFuture<>();
Runnable task = ()->{
try{
String htmlContent = fetchPageContent(url); //long running
futurePage.complete(htmlContent); //not with classicFuture
// this is why it is CompletableFut.
}catch(Throwable th){
futurePage.completeExceptionally(th); //set complete exceptionally
}
};
exec.submit(task);
return futurePage;
}
20. Exception Handling
• Exception Handling in CompletableFuture:
– exceptionally()
– handle()
– whenComplete()
• Time for Code !
22. ReentrantReadWriteLock
• ReentrantReadWriteLock Usage:
private SomeClass theData;
pri final ReadWriteLock myLocks =
new ReentrantReadWriteLock(true);
public void write(){
myLocks.writeLock().lock();
try{theData.write();}
finally{myLocks.writeLock().unlock();}
}
public void read(){
myLocks.readLock().lock();
try{theData.read();}
finally{myLocks.readLock().unlock();}
}
23. StampedLock
• JDK8 addition into package
java.util.concurrent.locks
– Works with versions of StampedLock – the
“Stamp”
– Modes
• Pessimistic Write
• Pessimistic Read
• Optimistic Read! (Optimization)
24. Pessimistic StampedLocking
Pessimistic Usage:
private SomeClass theData;
private final StampedLock lock=
new StampedLock(true);
public void write(){
long stamp = lock.writeLock();
try{theData.write();}
finally{lock.writeLock(stamp);}
}
public void read(){
long stamp = lock.readLock();
try{theData.read();}
finally{lock.unlockRead(stamp);}
}
25. Optimistic StampedLock
Optimistic usage:
• try OptimisticRead
• Validate optimisticRead stamp
• Based on the validation take further action.
• Lets Try!!!
private int p1, p2
StampedLock lock = new StampedLock();
public void read(){
long stamp = lock.tryOptimisticRead();
int l1 = p1;
int l2 = p2;
if(lock.validate(stamp)){ //validate the stamp (version)
process(l1, l2);
}else{
stamp = lock.readLock()//acquire pessimistic locks
try{l1 = p1;
l2= p2;
process(l1, l2);
}
finally{lock.unlockRead(stamp)}
}
}
26. Conclusion
• CompletableFuture is powerful
– Explore more
– Write your own RxJava
• StampedLock is more efficient and optimized
for particular use cases!