CPL 2016, week 6 Asynchronous execution Oleg Batrashev Institute of Computer Science, Tartu, Estonia March 14, 2016
Overview Studied so far: 1. Inter-thread visibility : JMM 2. Inter-thread synchronization : locks and monitors 3. Thread management : executors, tasks, cancelation 4. Inter-thread communication : confinements, queues, back pressure 5. Inter-thread collaboration : actors, inboxes, state diagrams Today: ◮ Asynchronous execution : callbacks, Pyramid of Doom, Java 8 promises. Next weeks: ◮ Performance considerations : asynchronous IO, Java 8 streams.
Asynchronous execution 118/145 Why asynchronous - Outline Asynchronous execution Why asynchronous Callback hell Lambdas in Java 8 Promises in Java 8 Promises within actors Promise libraries
Asynchronous execution 119/145 Why asynchronous - Synchronous vs asynchronous Synchronous execution – algorithm executes sequentially, one step after another in single thread res1 = doSomething1 () res2 = doSomething2 (res1) res3 = doSomething3 (res1) print (res2+res3) ◮ doSomething3 executes after doSomething2 ◮ even with optimizations, think of the execution this way Asynchronous execution – parts of the algorithm run separately, possibly in different threads ◮ execute doSomething2 and doSomething3 in separate threads and wait for their results ◮ solvable with Java Futures ◮ execute them in separate threads but release this thread until res2 and res3 are available, then execute printing ◮ not solvable with Java Futures
Asynchronous execution 120/145 Why asynchronous - Problems of synchronous execution ◮ parallelism in the algorithm is not utilized (solved with Java futures) ◮ current thread is not released even if nothing to do, consider res1 = doRequest1 () res2 = doRequest2 (res1) print (res2) ◮ if the thread is waiting for the result of request 1, it has nothing else to do ◮ the thread is not released, it may not be used elsewhere ◮ e.g. making 10000 network requests or waiting for data from 10000 web clients requires the same number of threads ◮ solution : release the thread between doRequest1() and doRequest2() ◮ how to do that?
Asynchronous execution 121/145 Why asynchronous - Authentication example: the algorithm Need for an algorithm with several steps that run over a network. Authentication with the salt and hash digest: 1. Client provides his name 2. Server checks if the name is ok and returns the salt ◮ the salt is just any random string 3. Client combines the salt and his password into single string, it then computes the hash for it. The hash is sent to the server. ◮ hash is a sequence of bytes 4. Server compares received hash with the locally computed hash. If they are equal, then the password was correct. The server sends resulting response to the client. ◮ password is in the server database 5. Client knows whether the authentication was successful Advantage : the password is never sent over the network.
Asynchronous execution 122/145 Why asynchronous - Authentication example: synchronous code // send user name conn. sendMessage (new AuthComm. LoginRequest ("Oleg")); // receive params or result response Message <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 response Message <Type > m2 = conn. readMessage (); { ResultResponse m = ( ResultResponse ) m2; System.out.println("Success "+m); return; }
Asynchronous execution 123/145 Callback hell - Outline Asynchronous execution Why asynchronous Callback hell Lambdas in Java 8 Promises in Java 8 Promises within actors Promise libraries
Asynchronous execution 124/145 Callback hell - Callback solution ◮ 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 ); } ◮ put into inline callbacks results in Pyramid of Doom (not Java code!) doRequest1 (void callback1(res1) { doRequest2 (res1 , void callback2(res2) { print (res2 ); }); });
Asynchronous execution 125/145 Callback hell - Authentication example: callbacks ◮ response with the salt to the login request public interface ParamsCallback { void onSalt(String salt ); } public void login(String username , ParamsCallback paramsCallback , ResultCallback resultCallback ) ◮ response with result to the auth request public interface ResultCallback { void onSuccess (); void onFailure(String error ); } public void authenticate (byte [] digest , ResultCallback callback) ◮ login may result in failure through the result callback: if the username is invalid
Asynchronous execution 126/145 Callback hell - Authentication example: Pyramid of Doom ◮ Java callbacks are 2 level, because they are in interfaces // send user name authProxy.login("Oleg", new ParamsCallback () { public void onSalt(String salt) { // send password digest String 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 ); ◮ not pleasant to read, not reusable ◮ last null is the callback where error handling for login should happen, omitted here
Asynchronous execution 127/145 Callback hell - Named callbacks ◮ explicit classes: reusable code, but more difficult to follow public void run () { // send user name authProxy.login("Oleg", new ParamsCallbackImpl (), null ); } class ParamsCallbackImpl implements ParamsCallback { public void onSalt(String salt) { // send password digest String 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 ); } } ◮ imagine if callbacks are split between different files
Asynchronous execution 128/145 Callback hell - Callback summary ◮ asynchronous execution requires code that will be executed later – callbacks ◮ inline callbacks result in the Pyramid of Doom, which is unpleasant to read ◮ named callbacks split sequential logic into separate methods, which is even more difficult to follow ◮ Java callback as an object with method – adds 2 levels to the Pyramid of Doom Java 8 solutions: ◮ Java 8 promises (CompletableFuture) solve the problem with unreadable and hard to follow code ◮ Java 8 lambdas solve the problem of 2-level callbacks
Asynchronous execution 129/145 Lambdas in Java 8 - Outline Asynchronous execution Why asynchronous Callback hell Lambdas in Java 8 Promises in Java 8 Promises within actors Promise libraries
Asynchronous execution 130/145 Lambdas in Java 8 - Java 8 lambdas ◮ Syntactic sugar for “inline” methods: (arg1 ,arg2 ,arg3) -> { code ;..; return res; } ◮ 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 (); ◮ However, the following is invalid, because need an interface: Object o = ()-> System.out.println("hello"); “ The target type of this expression must be a functional interface ” ◮ Java makes methods out of interface implementations ◮ 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 functional interfaces: ◮ obj::methodName ◮ this::methodName ◮ 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 name authProxy.login("Oleg", salt -> { // Java8 lambda // send password digest String 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 ); ◮ problem with success/failure result, interface should contain single method ◮ failure handling at each call may pollute the code ◮ Java 8 provides error forwarding for promises
Recommend
More recommend