Slide 1

Slide 1 text

Devoxx Antwerpen 2015 Concurrency Models on the JVM Lutz Hühnken @lutzhuehnken https://github.com/lutzh/concurrency_talk

Slide 2

Slide 2 text

#devoxx 2 Before we start: Why Concurrency is hard (demonstrated with printed books)

Slide 3

Slide 3 text

#devoxx 3

Slide 4

Slide 4 text

#devoxx 4

Slide 5

Slide 5 text

#devoxx 5

Slide 6

Slide 6 text

#devoxx 6

Slide 7

Slide 7 text

#devoxx 7 Provocative claim (for you to discuss over drinks later): We need a new standard concurrency model for Java!

Slide 8

Slide 8 text

#devoxx 8 Thread Problems Problem 1: Efficiency (resp. the lack thereof)

Slide 9

Slide 9 text

#devoxx 9 CPU Threads Work

Slide 10

Slide 10 text

#devoxx 10 1 2 3 … 10.000 4-core CPU Thousands of Threads Work (a lot)

Slide 11

Slide 11 text

#devoxx 11 We want to move towards this:

Slide 12

Slide 12 text

#devoxx 12 From Thread Process Process Worker Task To Also see https://en.wikipedia.org/wiki/Reactor_pattern

Slide 13

Slide 13 text

#devoxx Thinking about blocking I/O When one thread corresponds to one task 13 Note: This is a snapshot in time of the system, not a sequence of events

Slide 14

Slide 14 text

#devoxx Never block! When one thread is a worker, working on multiple tasks 14 Note: This is a snapshot in time of the system, not a sequence of events

Slide 15

Slide 15 text

#devoxx Solution for the efficiency problem: • Sub-thread level concurrency • Asynchronous I/O
 • This is something the approaches we will look at have in common
 • Note: This is also something that all Reactive Systems* have in common
 * http://www.reactivemanifesto.org 15

Slide 16

Slide 16 text

#devoxx 16 Thread Problems Problem 1: Efficiency (resp. the lack thereof) Problem 2: Programming Model

Slide 17

Slide 17 text

#devoxx 17 They discard the most essential and appealing properties of sequential computation: understandability, predictability, and determinism. Threads, as a model of computation, are wildly nondeterministic, and the job of the programmer becomes one of pruning that nondeterminism.

Slide 18

Slide 18 text

#devoxx 18 Source: John Rose, Java VM Architect, JFokus, Stockholm, February 2015

Slide 19

Slide 19 text

#devoxx Digression: Callback Hell fs.readdir(source, function(err, files) { if (err) { console.log('Error finding files: ' + err) } else { files.forEach(function(filename, fileIndex) { console.log(filename) gm(source + filename).size(function(err, values) { if (err) { console.log('Error identifying file size: ' + err) } else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function(width, widthIndex) { height = Math.round(width / aspect) console.log('resizing ' + filename + 'to ' + height + 'x' + height) this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err) }) }.bind(this)) } }) }) } }) 19

Slide 20

Slide 20 text

#devoxx I admit: It could be written in a more readable way • Scala Futures / for-expressions • async / await (also Scala) • JavaScript Promises (then.. then.. then)
 • All this is syntactic sugar for callbacks - which is good • But let’s explore other (better) approaches for composition 20

Slide 21

Slide 21 text

#devoxx 21 What are we interested in? Composition State Management Integration, I/O (both async and sync) Predictability

Slide 22

Slide 22 text

#devoxx 22 Coroutines (Green Threads, User Mode Threads, IOC Threads, Fibers) Quasar Fibers http://www.paralleluniverse.co/quasar/ Channels Clojure core.async https://github.com/clojure/core.async/ Event Loop vert.x http://vertx.io Actor Model Akka http://akka.io Concurrency Models

Slide 23

Slide 23 text

Fibers

Slide 24

Slide 24 text

#devoxx 24 new Fiber() { @Override protected V run() throws SuspendExecution, InterruptedException { // code here } }.start(); looks like a thread, walks like a thread..

Slide 25

Slide 25 text

#devoxx 25 class RetrieveInfo extends Fiber { ... @Override public Void run() { final CloseableHttpClient client = FiberHttpClientBuilder.create().build(); try { String response = client.execute(new HttpGet(MessageFormat.format(url, searchTerm)), new BasicResponseHandler()); addResult(key, StringUtils.abbreviate(response, 1000)); } catch (IOException iox) { ... Use like blocking Need to aggregate results.. „fiber blocking“ async http client

Slide 26

Slide 26 text

#devoxx 26 private final Map store = new ConcurrentHashMap<>(); public void define(final String searchTerm) { for (String currentKey : sources.keySet()) { new RetrieveInfo(currentKey, sources.get(currentKey), searchTerm).start(); } } /** * This method is working on shared mutual state - this is what we look to avoid! */ void addResult(final String key, final String result) { store.put(key, result); ... This method is working on shared mutual state - this is what we look to shared mutable state Start retrieval of definitions in background

Slide 27

Slide 27 text

#devoxx Fibers • Major advantage: Imperative Programming, just like with threads
 • Major disadvantage: Imperative Programming, just like with threads 27

Slide 28

Slide 28 text

#devoxx 28 class FooAsync extends FiberAsync implements FooCompletion { @Override public void success(String result) { asyncCompleted(result); } @Override public void failure(FooException exception) { asyncFailed(exception); } } String op() { new FooAsync() { protected void requestAsync() { Foo.asyncOp(this); } }.run(); } Turn callback APIs into fiber blocking APIs!

Slide 29

Slide 29 text

#devoxx Fibers Summary • Thread-like programming model • Nice „tricks“: Instrumentation, Suspendable, Thread Interop • Fibers alone are a bit low-level • (Almost) drop-in replacement for threads 29

Slide 30

Slide 30 text

Channels

Slide 31

Slide 31 text

#devoxx Channels • Theoretical Foundation:
 Communicating Sequential Processes (Tony Hoare 1978, https:// en.wikipedia.org/wiki/ Communicating_sequential_processes) • Very popular in the „Go“ community • Our example: Clojure core.async 31 Channels

Slide 32

Slide 32 text

#devoxx 32 (def echo-chan (chan)) (go (println (!! echo-chan „ketchup") ; => true ; => ketchup go block - like a fiber >!! blocks thread

Slide 33

Slide 33 text

#devoxx 33 (def echo-buffer (chan 2)) (>!! echo-buffer "ketchup") ; => true (>!! echo-buffer "ketchup") ; => true (>!! echo-buffer "ketchup") ; blocks buffered channel - async

Slide 34

Slide 34 text

#devoxx 34 (def responses-channel (chan 10)) (go (println (loop [values []] (if (= 3 (count values)) values (recur (conj values (! result (:body %))))) (async-get "http:m-w.com..." responses-channel) (async-get "http:wiktionary.." responses-channel) (async-get "http:urbandictionary.." responses-channel) callback puts result in channel channel for responses Aggregate results recursively

Slide 35

Slide 35 text

#devoxx Channels • Allows flow-oriented programming, very flexible composition (e.g. alts!) • Sanity through sequencing • go block / thread duality allows integration with both async and sync libraries • Channels as keepers of state? 35 Channels Summary

Slide 36

Slide 36 text

Event Loop

Slide 37

Slide 37 text

#devoxx Channels • Event loop model - think node.js • „multi event loop“, on the JVM • Polyglot - Java, Scala, Clojure, JRuby, JS.. • Deploy small pieces (Verticles) and have them send JSON to each other 37 vert.x

Slide 38

Slide 38 text

#devoxx 38 public class Receiver extends AbstractVerticle { @Override public void start() throws Exception { EventBus eb = vertx.eventBus(); eb.consumer("ping-address", message -> { System.out.println("Received message: " + message.body()); // Now send back reply message.reply("pong!"); }); System.out.println("Receiver ready!"); } } register on event bus extend Verticle connect

Slide 39

Slide 39 text

#devoxx 39 MessageConsumer consumer = eventBus.consumer("definer.task"); consumer.handler(message -> { httpClient.getNow(message.body().getString("host"), message.body().getString("path"), httpClientResponse -> { httpClientResponse.bodyHandler(buffer -> { eventBus.send("collector", new JsonObject() .put(...) callback based async http register send result as message

Slide 40

Slide 40 text

#devoxx 40 Vertx vertx = Vertx.vertx(); EventBus eventBus = vertx.eventBus(); DeploymentOptions options = new DeploymentOptions().setInstances(sources.size()); vertx.deployVerticle("de.huehnken.concurrency.Task", options, msg -> { for (String currentKey : sources.keySet()) { eventBus.send("definer.task", new JsonObject() .put(..) which one gets the message? deploy multiple

Slide 41

Slide 41 text

#devoxx 41 private final Map store = new HashMap<>(); @Override public void start(Future startFuture) { EventBus eventBus = vertx.eventBus(); MessageConsumer consumer = eventBus.consumer("collector"); consumer.handler(message -> { store.put(message.body().getString(„key"), message.body().getString("definition")); wait for results.. encapsultate state in Verticle messages are usually JSON

Slide 42

Slide 42 text

#devoxx Event Bus (vert.x) 42 Image from Jonas Bandi @jbandi

Slide 43

Slide 43 text

#devoxx Channels • Loose coupling - very • Sanity through events („single thread illusion“) • Hybrid thread model allows integration of synchronous APIs, off-loading work from event loop • State handling needs some care • Utility APIs are callback heavy, node inspired • Distributed! Unified model for local and distributed concurrency 43 vert.x Summary

Slide 44

Slide 44 text

Actors

Slide 45

Slide 45 text

#devoxx Channels • The actor model [..] is a [model] that treats "actors" as the universal primitives of concurrent computation: in response to a message that it receives, an actor can make local decisions, create more actors, send more messages, and determine how to respond to the next message received. https://en.wikipedia.org/wiki/Actor_model • Described by Carl Hewitt 1973 • Popular in the Erlang community 45 Akka

Slide 46

Slide 46 text

#devoxx Actors (Akka) 46

Slide 47

Slide 47 text

#devoxx Actors (Akka) 47

Slide 48

Slide 48 text

#devoxx Actors (Akka) 48

Slide 49

Slide 49 text

#devoxx 49 class Coordinator(searchTerm: String) extends Actor { var results = Map[String, String]() def receive = { case StartCommand => sources foreach { case (key, url) => context.actorOf(Task.props(key, self)) ! GetDefinition(url) } context.system.scheduler.scheduleOnce(5 seconds, self, Done) case Result(key, definition) => results += key -> definition if (results.size == sources.size) { self ! Done } case Done => spawn child actors, send command encapsulate state aggregate results

Slide 50

Slide 50 text

#devoxx 50 class Task(key: String, coordinator: ActorRef) extends Actor { def receive = { case GetDefinition(url) => http.singleRequest(HttpRequest(uri = url)) pipeTo self case HttpResponse(StatusCodes.OK, headers, entity, _) => coordinator ! Result(key, entity.dataBytes.utf8String) case HttpResponse(code, _, _, _) => // handle error send result to aggregator async call, translated to message

Slide 51

Slide 51 text

#devoxx Actors (Akka) 51

Slide 52

Slide 52 text

#devoxx Channels • Sanity through messages („single thread illusion“) • Messages (as opposed to events) • Dispatchers allows integration of synchronous APIs, off-loading work from event loop • pipeTo & ask to integrate with async APIs • Distributed! Unified model for local and distributed concurrency. • Supervision! Fault handling enforced. 52 Actors Summary

Slide 53

Slide 53 text

#devoxx 53 Programming model chart Tasks (sub- thread level) Asynchronous Messaging Distribution (unified model) Supervision Fibers ✔ Channels (core.async) ✔ ✔ Event Bus (vert.x) ✔ ✔ ✔ Aktoren (Akka) ✔ ✔ ✔ ✔

Slide 54

Slide 54 text

#devoxx • Concurrency is interesting
 • Threads are passé, we want a better abstraction on top of it • Alternatives are available today
 • If you only have time to look at one model, I recommend Akka 54 Summary

Slide 55

Slide 55 text

#devoxx 55 Closing the loop.. another book..

Slide 56

Slide 56 text

Thank You Lutz Hühnken @lutzhuehnken https://github.com/lutzh/concurrency_talk

Slide 57

Slide 57 text

Agents

Slide 58

Slide 58 text

#devoxx 58 (def x (agent 0)) (defn increment [c n] (+ c n)) (send x increment 5) ; @x -> 5 (send x increment 10) ; @x -> 15 „wrap“ a value in an agent send the code to the agent to modify value! a function

Slide 59

Slide 59 text

#devoxx Agents • The agent encapsulates state • Send code to the agent to change value • Interesting feature especially for functional programming • Async execution, but not really a concurrency model 59

Slide 60

Slide 60 text

#devoxx Beware of the benchmark! • Unfit: CPU bound • Also unfit: Anything using blocking I/O • It’s not about single request performance, it’s about scalability! 60

Slide 61

Slide 61 text

#devoxx But I just want to build a little REST service.. • If you don’t program on this level yourself: know you frameworks! • Look out for „reactive“ or „async“ • Play! • Vert.x • Akka HTTP • (Pivotal Reactor ? Rat pack ?) 61

Slide 62

Slide 62 text

#devoxx For completeness • Quasar also offers Channels, and even Actors • Akka also offers a Bus, and Agents. • A lot of this comes from outside of the JVM (Go Channels, node.js, Erlang Actors) 62

Slide 63

Slide 63 text

Thanks Again! Lutz Hühnken @lutzhuehnken https://github.com/lutzh/concurrency_talk

Slide 64

Slide 64 text

#devoxx Image Credits Cat on hot tin roof - from "McPhillamyActorBlog" - http://mcphillamy.com Agents - from "Matrix Wiki" - http://matrix.wikia.com/wiki/Agent Event Loop - http://www.freeimages.com/photo/big-looping-1522456 Channel - http://www.freeimages.com/photo/white-cliffs-of-dover-1256973 Fibers - http://www.freeimages.com/photo/fiber-optics-1503081 64