CPL 2016, week 6 - Asynchronous execution€¦ · CPL 2016, week 6 Asynchronousexecution...
Transcript of CPL 2016, week 6 - Asynchronous execution€¦ · CPL 2016, week 6 Asynchronousexecution...
CPL 2016, week 6Asynchronous execution
Oleg Batrashev
Institute of Computer Science, Tartu, Estonia
March 14, 2016
Overview
Studied so far:1. Inter-thread visibility: JMM2. Inter-thread synchronization: locks and monitors3. Thread management: executors, tasks, cancelation4. Inter-thread communication: confinements, queues, back
pressure5. Inter-thread collaboration: actors, inboxes, state diagrams
Today:I Asynchronous execution: callbacks, Pyramid of Doom, Java
8 promises.Next weeks:
I Performance considerations: asynchronous IO, Java 8streams.
Asynchronous execution 118/145 Why asynchronous -
Outline
Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries
Asynchronous execution 119/145 Why asynchronous -
Synchronous vs asynchronousSynchronous execution – algorithm executes sequentially, one stepafter another in single thread
res1 = doSomething1 ()res2 = doSomething2(res1)res3 = doSomething3(res1)print (res2+res3)
I doSomething3 executes after doSomething2I even with optimizations, think of the execution this way
Asynchronous execution – parts of the algorithm run separately,possibly in different threads
I execute doSomething2 and doSomething3 in separate threadsand wait for their results
I solvable with Java Futures
I execute them in separate threads but release this thread untilres2 and res3 are available, then execute printing
I not solvable with Java Futures
Asynchronous execution 120/145 Why asynchronous -
Problems of synchronous execution
I parallelism in the algorithm is not utilized (solved with Javafutures)
I current thread is not released even if nothing to do, consider
res1 = doRequest1 ()res2 = doRequest2(res1)print (res2)
I if the thread is waiting for the result of request 1, it hasnothing else to do
I the thread is not released, it may not be used elsewhereI e.g. making 10000 network requests or waiting for data from
10000 web clients requires the same number of threads
I solution: release the thread between doRequest1() anddoRequest2()
I how to do that?
Asynchronous execution 121/145 Why asynchronous -
Authentication example: the algorithmNeed for an algorithm with several steps that run over a network.Authentication with the salt and hash digest:1. Client provides his name2. Server checks if the name is ok and returns the salt
I the salt is just any random string
3. Client combines the salt and his password into single string, itthen computes the hash for it. The hash is sent to the server.
I hash is a sequence of bytes
4. Server compares received hash with the locally computedhash. If they are equal, then the password was correct. Theserver sends resulting response to the client.
I password is in the server database
5. Client knows whether the authentication was successfulAdvantage: the password is never sent over the network.
Asynchronous execution 122/145 Why asynchronous -
Authentication example: synchronous code// send user nameconn.sendMessage(new AuthComm.LoginRequest("Oleg"));// receive params or result responseMessage <Type > m1 = conn.readMessage ();if (m1.type==Type.RES_RESULT) {
ResultResponse m = (ResultResponse) m1;System.out.println("Success "+m);return;
}{ // send password digest
ParamsResponse m = (ParamsResponse) m1;MessageDigest digestAlgo = MessageDigest.getInstance("SHA -1");String password = "mypassword";byte[] digest = digestAlgo.digest ((m.salt+password ). getBytes ());conn.sendMessage(new AuthComm.AuthRequest(digest ));
}// receive result responseMessage <Type > m2 = conn.readMessage ();{
ResultResponse m = (ResultResponse) m2;System.out.println("Success "+m);return;
}
Asynchronous execution 123/145 Callback hell -
Outline
Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries
Asynchronous execution 124/145 Callback hell -
Callback solutionI give the method another one which should be executed when
the operations completes
doRequest1(callback1 );
void callback1(res1) {doRequest2(res1 , callback2 );
}
void callback2(res2) {print (res2);
}
I put into inline callbacks results in Pyramid of Doom (not Javacode!)
doRequest1(void callback1(res1) {doRequest2(res1 , void callback2(res2) {
print (res2);});
});
Asynchronous execution 125/145 Callback hell -
Authentication example: callbacks
I response with the salt to the login request
public interface ParamsCallback {void onSalt(String salt);
}public void login(String username ,
ParamsCallback paramsCallback ,ResultCallback resultCallback)
I response with result to the auth request
public interface ResultCallback {void onSuccess ();void onFailure(String error );
}public void authenticate(byte[] digest ,
ResultCallback callback)
I login may result in failure through the result callback: if theusername is invalid
Asynchronous execution 126/145 Callback hell -
Authentication example: Pyramid of DoomI Java callbacks are 2 level, because they are in interfaces
// send user nameauthProxy.login("Oleg", new ParamsCallback () {
public void onSalt(String salt) {// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt+password ). getBytes ());authProxy.authenticate(digest , new ResultCallback () {
public void onSuccess () {System.out.println("Login was successful");
}public void onFailure(String error) {
System.out.println("Login failed: "+error);}
});}
}, null);
I not pleasant to read, not reusableI last null is the callback where error handling for login should
happen, omitted here
Asynchronous execution 127/145 Callback hell -
Named callbacksI explicit classes: reusable code, but more difficult to follow
public void run() { // send user nameauthProxy.login("Oleg", new ParamsCallbackImpl (), null);
}class ParamsCallbackImpl implements ParamsCallback {
public void onSalt(String salt) {// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt + password ). getBytes ());authProxy.authenticate(digest , new ResultCallbackImpl ());
}}class ResultCallbackImpl implements ResultCallback {
public void onSuccess () {System.out.println("Login was successful");
}public void onFailure(String error) {
System.out.println("Login failed: " + error);}
}
I imagine if callbacks are split between different files
Asynchronous execution 128/145 Callback hell -
Callback summary
I asynchronous execution requires code that will be executedlater – callbacks
I inline callbacks result in the Pyramid of Doom, which isunpleasant to read
I named callbacks split sequential logic into separate methods,which is even more difficult to follow
I Java callback as an object with method – adds 2 levels to thePyramid of Doom
Java 8 solutions:I Java 8 promises (CompletableFuture) solve the problem with
unreadable and hard to follow codeI Java 8 lambdas solve the problem of 2-level callbacks
Asynchronous execution 129/145 Lambdas in Java 8 -
Outline
Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries
Asynchronous execution 130/145 Lambdas in Java 8 -
Java 8 lambdasI Syntactic sugar for “inline” methods:
(arg1 ,arg2 ,arg3) -> { code ;..; return res; }
I It is possible to write:Runnable r = ()->System.out.println("hello");Callable <String > c1 = ()->"hello";Callable <String > c2 = ()-> {
System.out.println("in c2");return "hello";
};r.run (); c1.call (); c2.call ();
I However, the following is invalid, because need an interface:Object o = ()->System.out.println("hello");
“The target type of this expression must be a functionalinterface”
I Java makes methods out of interface implementationsI functional interface[1, sec 1.3] must contain single method
declaration
Asynchronous execution 131/145 Lambdas in Java 8 -
Java 8 method references
Java 8 provides nice way to reference methods as functionalinterfaces:
I obj::methodNameI this::methodName
I ClassName::staticMethodName
java.util.function interfaces:
Function <Integer ,Integer > m1 = Math::abs;BiFunction <Random ,Integer ,Integer > m2 = Random :: nextInt;BiConsumer <PrintStream ,char[]> m3 = PrintStream :: print;
Asynchronous execution 132/145 Lambdas in Java 8 -
Lambdas in Pyramid of Doom
// send user nameauthProxy.login("Oleg", salt -> { // Java8 lambda
// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt+password ). getBytes ());authProxy.authenticate(digest , new ResultCallback () {
public void onSuccess () {System.out.println("Login was successful");
}public void onFailure(String error) {
System.out.println("Login failed: "+error);}
});}, null);
I problem with success/failure result, interface should containsingle method
I failure handling at each call may pollute the codeI Java 8 provides error forwarding for promises
Asynchronous execution 133/145 Promises in Java 8 -
Outline
Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries
Asynchronous execution 134/145 Promises in Java 8 -
Problems of Java Future
I Java Future is the result of task executionI it provides the result once execution is completedI must submit a task before you can have its futureI what if want to return the future for the task executed third in
the sequence of async steps
I Java Future does not providefut.runThisCallback(callback) methods
I i.e. can only wait with fut.get() blocking the thread
Java 8 CompletableFuture (promise):I may be completed at any time by promise.complete(res)
I only first result is preserved and valid
I have a bunch of promise.thenApply(callback) methodsI all registered callbacks are executed when promise is completed
Asynchronous execution 135/145 Promises in Java 8 -
Java 8 CompletableFuturejava.util.concurrent.CompletableFuture<Result>
I like Java Future completed once but no specific task requiredI use complete(), completeExceptionally(), cancel()
I like java Future may be waited with get() or get(t,unit)I unlike Java Futures “continuations” may be added to it
CF <Void > thenRun(Runnable action)CF <Void > thenAccept(Consumer <? super T> action)CF <U> thenApply(Function <? super T,? extends U> fn)CF <U> thenCompose(
Function <? super T,? extends CompletionStage <U>> fn)CF <U> handle(BiFunction <? super T,Throwable ,? extends U> fn)CF <T> whenComplete(
BiConsumer <? super T,? super Throwable > action)
I the callbacks are executed after the promise is completed
I create the promiseI completedFuture(), runAsync(), supplyAsync()
Asynchronous execution 136/145 Promises in Java 8 -
Apply vs composeAfter a promise has completed run a function with the result:
I apply callback returns (? extends U)I compose callback returns (? extends CompletionStage<U>)I both take value of type T from the source promise
Imagine we have CompletableFuture<String> is a username we getasynchronously (e.g. receive from network)Want to check the validity of a username:1. Boolean checkLocally(String username) – only validates that
characters are ok, may be done synchronously2. CF<Boolean> checkRemotely(String username) – validates
that username exists in a database, the result comesasynchronously
Need to use apply in the first case and compose in the second:1. unamePromise.thenApply(this::checkLocally)2. unamePromise.thenCompose(this::checkRemotely)
Otherwise, compose returns CF<CF<Boolean>> – need to flatten
Asynchronous execution 137/145 Promises in Java 8 -
Handle vs whenComplete
I whenComplete() returns the old futureI i.e. it invokes the callback, but does not enhance the callback
chain
I handle() does return the new futureI this may be used to run actions when async action defined in
the handle method completes
Asynchronous execution 138/145 Promises in Java 8 -
Authentication example: promise chainingI use thenCompose and handle to chain promisesI error in the chain is propagated to the last promise omitting all
subsequent callbacks in the chain
// send user nameCompletableFuture <String > saltPromise = authProxy.login("Oleg");CompletableFuture <Void > resultPromise =
saltPromise.thenCompose(salt -> {// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt+password ). getBytes ());return authProxy.authenticate(digest );
});// handle all errors or final successresultPromise.handle ((v, err) -> {
if (err==null)System.out.println("Login was successful");
elseSystem.out.println("Login failed: "+err);
return null;});
Asynchronous execution 139/145 Promises in Java 8 -
Promise chaining with separate methodsI easier to see the overall process
public void doAuth () throws IOException {authProxy.connect("localhost", 5000);CompletableFuture.completedFuture("Oleg")
.thenCompose(authProxy ::login)
.thenCompose(this:: sendHashFromSaltAndPassword)
.handle(this:: printResult );}CompletableFuture <Void > sendHashFromSaltAndPassword(String salt) {
// send password digestString password = "mypassword";byte[] digest = digestAlgo.digest ((salt+password ). getBytes ());return authProxy.authenticate(digest );
}Void printResult(Void v, Throwable err) {
if (err==null)System.out.println("Login was successful");
elseSystem.out.println("Login failed: "+err);
return null;}
Asynchronous execution 140/145 Promises within actors -
Outline
Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries
Asynchronous execution 141/145 Promises within actors -
Promise executors
Almost all methods provide 3 flavors, e.g. for run:
CF<Void > thenRun(Runnable action );CF<Void > thenRunAsync(Runnable action );CF<Void > thenRunAsync(Runnable action , Executor executor );
I action may be executed in the thread that has completed thepromise
I action is executed by the ForkJoinPool.commonPool()I action is executed by the provided executor
We must think about the consequences of synchronization or thelack of it!
Asynchronous execution 142/145 Promises within actors -
Promises within actors
I actor is inherently asynchronousI its logic is split between message handling code pieces
I often it is more convenient to program sequence of actions,like with authentication
I use promise chains inside an actor
Pay attention:I callbacks must be executed in the actor thread,I current state is not automatically checked when the callback is
executed,I consider promise (chain) cancellation
I if done from the actor thread, the rest of the promise chainwon’t be executed
Asynchronous execution 143/145 Promise libraries -
Outline
Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries
Asynchronous execution 144/145 Promise libraries -
Libraries
I Akka futures –http://doc.akka.io/docs/akka/2.4.2/java/futures.html
I RxJava – https://github.com/ReactiveX/RxJava/wikiI ReactiveX – http://reactivex.io/intro.html
Asynchronous execution 145/145 Promise libraries -
Summary
I asynchronous execution is necessary to utilize more resourcesand release threads when not needed
I callbacks allow to “continue” execution, but it results inI Pyramid of Doom or very scattered callback code
I Java futures help with resource utilization but not threadrelease
I Java 8 completable future (promise) solves the problemI allow to add callbacks through the then*() methods
I callbacks are called in other threads, which is especially criticalfor actors