CPL 2016, week 6 Asynchronous execution Oleg Batrashev Institute - - PowerPoint PPT Presentation

cpl 2016 week 6
SMART_READER_LITE
LIVE PREVIEW

CPL 2016, week 6 Asynchronous execution Oleg Batrashev Institute - - PowerPoint PPT Presentation

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


slide-1
SLIDE 1

CPL 2016, week 6

Asynchronous execution Oleg Batrashev

Institute of Computer Science, Tartu, Estonia

March 14, 2016

slide-2
SLIDE 2

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.

slide-3
SLIDE 3

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

slide-4
SLIDE 4

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

slide-5
SLIDE 5

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?

slide-6
SLIDE 6

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.

slide-7
SLIDE 7

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; }

slide-8
SLIDE 8

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

slide-9
SLIDE 9

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 ); }); });

slide-10
SLIDE 10

Asynchronous execution 125/145 Callback hell -

Authentication example: callbacks

◮ response with the salt to the login request public interface ParamsCallback { void

  • nSalt(String

salt ); } public void login(String username , ParamsCallback paramsCallback , ResultCallback resultCallback ) ◮ response with result to the auth request public interface ResultCallback { void

  • nSuccess ();

void

  • nFailure(String

error ); } public void authenticate (byte [] digest , ResultCallback callback) ◮ login may result in failure through the result callback: if the

username is invalid

slide-11
SLIDE 11

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

  • nSalt(String

salt) { // send password digest String password = " mypassword "; byte [] digest = algo.digest (( salt+password ). getBytes ());

  • authProxy. authenticate (digest , new

ResultCallback () { public void

  • nSuccess () {

System.out.println("Login was successful"); } public void

  • nFailure(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

slide-12
SLIDE 12

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

  • nSalt(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

  • nSuccess () {

System.out.println("Login was successful"); } public void

  • nFailure(String

error) { System.out.println("Login failed: " + error ); } } ◮ imagine if callbacks are split between different files

slide-13
SLIDE 13

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

slide-14
SLIDE 14

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

slide-15
SLIDE 15

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

slide-16
SLIDE 16

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;

slide-17
SLIDE 17

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

  • nSuccess () {

System.out.println("Login was successful"); } public void

  • nFailure(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

slide-18
SLIDE 18

Asynchronous execution 133/145 Promises in Java 8 -

Outline

Asynchronous execution Why asynchronous Callback hell Lambdas in Java 8 Promises in Java 8 Promises within actors Promise libraries

slide-19
SLIDE 19

Asynchronous execution 134/145 Promises in Java 8 -

Problems of Java Future

◮ Java Future is the result of task execution

◮ it provides the result once execution is completed ◮ must submit a task before you can have its future ◮ what if want to return the future for the task executed third in

the sequence of async steps

◮ Java Future does not provide

fut.runThisCallback(callback) methods

◮ i.e. can only wait with fut.get() blocking the thread

Java 8 CompletableFuture (promise):

◮ may be completed at any time by promise.complete(res)

◮ only first result is preserved and valid

◮ have a bunch of promise.thenApply(callback) methods

◮ all registered callbacks are executed when promise is completed

slide-20
SLIDE 20

Asynchronous execution 135/145 Promises in Java 8 -

Java 8 CompletableFuture

java.util.concurrent.CompletableFuture<Result>

◮ like Java Future completed once but no specific task required

◮ use complete(), completeExceptionally(), cancel()

◮ like java Future may be waited with get() or get(t,unit) ◮ 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)

◮ the callbacks are executed after the promise is completed

◮ create the promise

◮ completedFuture(), runAsync(), supplyAsync()

slide-21
SLIDE 21

Asynchronous execution 136/145 Promises in Java 8 -

Apply vs compose

After a promise has completed run a function with the result:

◮ apply callback returns (? extends U) ◮ compose callback returns (? extends CompletionStage<U>) ◮ both take value of type T from the source promise

Imagine we have CompletableFuture<String> is a username we get asynchronously (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 synchronously

  • 2. CF<Boolean> checkRemotely(String username) – validates

that username exists in a database, the result comes asynchronously 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

slide-22
SLIDE 22

Asynchronous execution 137/145 Promises in Java 8 -

Handle vs whenComplete

◮ whenComplete() returns the old future

◮ i.e. it invokes the callback, but does not enhance the callback

chain

◮ handle() does return the new future

◮ this may be used to run actions when async action defined in

the handle method completes

slide-23
SLIDE 23

Asynchronous execution 138/145 Promises in Java 8 -

Authentication example: promise chaining

◮ use thenCompose and handle to chain promises ◮ error in the chain is propagated to the last promise omitting all

subsequent callbacks in the chain

// send user name CompletableFuture <String > saltPromise = authProxy.login("Oleg"); CompletableFuture <Void > resultPromise = saltPromise . thenCompose (salt

  • > {

// send password digest String password = " mypassword "; byte [] digest = algo.digest (( salt+password ). getBytes ()); return

  • authProxy. authenticate (digest );

}); // handle all errors or final success resultPromise .handle ((v, err) -> { if (err == null) System.out.println("Login was successful"); else System.out.println("Login failed: "+err ); return null; });

slide-24
SLIDE 24

Asynchronous execution 139/145 Promises in Java 8 -

Promise chaining with separate methods

◮ 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 digest String 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"); else System.out.println("Login failed: "+err ); return null; }

slide-25
SLIDE 25

Asynchronous execution 140/145 Promises within actors -

Outline

Asynchronous execution Why asynchronous Callback hell Lambdas in Java 8 Promises in Java 8 Promises within actors Promise libraries

slide-26
SLIDE 26

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 ); ◮ action may be executed in the thread that has completed the

promise

◮ action is executed by the ForkJoinPool.commonPool() ◮ action is executed by the provided executor

We must think about the consequences of synchronization or the lack of it!

slide-27
SLIDE 27

Asynchronous execution 142/145 Promises within actors -

Promises within actors

◮ actor is inherently asynchronous

◮ its logic is split between message handling code pieces

◮ often it is more convenient to program sequence of actions,

like with authentication

◮ use promise chains inside an actor

Pay attention:

◮ callbacks must be executed in the actor thread, ◮ current state is not automatically checked when the callback is

executed,

◮ consider promise (chain) cancellation

◮ if done from the actor thread, the rest of the promise chain

won’t be executed

slide-28
SLIDE 28

Asynchronous execution 143/145 Promise libraries -

Outline

Asynchronous execution Why asynchronous Callback hell Lambdas in Java 8 Promises in Java 8 Promises within actors Promise libraries

slide-29
SLIDE 29

Asynchronous execution 144/145 Promise libraries -

Libraries

◮ Akka futures –

http://doc.akka.io/docs/akka/2.4.2/java/futures.html

◮ RxJava – https://github.com/ReactiveX/RxJava/wiki ◮ ReactiveX – http://reactivex.io/intro.html

slide-30
SLIDE 30

Asynchronous execution 145/145 Promise libraries -

Summary

◮ asynchronous execution is necessary to utilize more resources

and release threads when not needed

◮ callbacks allow to “continue” execution, but it results in

◮ Pyramid of Doom or very scattered callback code

◮ Java futures help with resource utilization but not thread

release

◮ Java 8 completable future (promise) solves the problem

◮ allow to add callbacks through the then*() methods

◮ callbacks are called in other threads, which is especially critical

for actors