Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Concurrency on the JVM

Concurrency on the JVM

A brief apetizer on why concurrency is important, and interesting things to know about.

Avatar for Niels Bech Nielsen

Niels Bech Nielsen

November 14, 2012

More Decks by Niels Bech Nielsen

Other Decks in Programming

Transcript

  1. About... • 20+ experience in object oriented software development •

    Teaching datamatician and industry • Former Global Chief Engineer @ JP Morgan Rates in Glasgow ◦ Intraday Risk Management ◦ Trade capture
  2. The Need for Speed • User response ◦ Single-threaded UI

    • Resource Utilisation ◦ I/O, Stale time etc • Parallel Execution ◦ multi-core
  3. Need for Speed II • Many cumulative tasks have never

    benefitted from concurrent scheduling ◦ Complexity in code ◦ Overhead in time • Parallel execution on multiple cores will always be more beneficial ◦ It may be complex to do ◦ But it would be an overhead not to do so
  4. Java concurrency • Until Java 5 very primitive ◦ Manual

    (and somewhat dangerous) thread model ◦ Object-Monitor synchronization ◦ Wait-Notify paradigm • Java 5++ ◦ Tools for processing ▪ Executors, Callables, Futures ◦ Tools for race conditions ▪ ReadWriteLocks, atomics etc ◦ Tools for recursive processing ▪ (Fork-Join) ◦ Tools for synchronisation ▪ Semaphores, CyclicBarriers etc
  5. Task scheduling • Best utilisation based on ◦ Number of

    processors/cores ▪ Runtime.getRuntime().availableProcessors(); ◦ Blocking coefficiency ▪ The degree of blocking I/O operations poolsize = number of cores / (1 - blocking coefficient)
  6. Task recursion • Functional paradigms are sometimes deemed more suitable

    for parallel execution ◦ Collection processing ◦ Recursion • Fork-Join model of Java 7 ◦ Recurse task invocation ◦ Without thread starvation (work stealing algo) doTask : collection - if empty return zero - else return head' + doTask tail if (my portion of the work is small enough) do the work directly else split my work into two pieces invoke the two pieces and wait for the results
  7. Fork-Join example class Fibonacci extends RecursiveTask<Integer> { final int n;

    Fibonnaci(int n) { this.n = n; } Integer compute() { if (n <= 1) return n; Fibonacci f1 = new Fibonacci(n - 1); f1.fork(); Fibonacci f2 = new Fibonacci(n - 2); return f2.invoke() + f1.join(); } } ForkJoinPool pool = new ForkJoinPool(); pool.invoke(new Fibonacci(100));
  8. Race Conditions • Race Conditions are ◦ always non-deterministic ◦

    Usually sporadic ◦ Hard to spot in code ◦ Usually distant from problem • Occurs when ◦ More than one thread ◦ Working on common data ◦ At least one write operation Remove one condition and problem is solved
  9. Sharing State • Shared mutability ◦ All threads access and

    write data directly • Isolated mutability ◦ Only 1 thread access and write data • Immutability ◦ What is written is unchangeable ◦ By means of final keywords ◦ copy on write collections ◦ Linked Lists, Tries
  10. Shared mutability • Usually very unavoidable • Previously only had

    few tools ◦ synchronized ◦ volatile (happens-before until java 1.4) • But somewhat updated ◦ volatile (synchronizes-with after 1.5) ◦ AtomicXXX ◦ Locks • With additional paradigms
  11. New Paradigms Lock: Atomics: lock.readLock().lock(); try { ... } finally

    { lock.readLock().unlock(); } boolean updated = false; while(!updated) { long oldValue = atom.get(); long newValue = oldValue * 1.25; // Add VAT updated = atom.compareAndSet(oldValue, newValue); } (Note: increment and decrements are safe)
  12. Cost of Contention Increment counter 500 000 000 times 1

    thread 300 ms 1 thread volatile 4700 ms (15x) 1 thread atomic 5700 ms (19x) 1 thread lock 10000 ms (33x) 2 threads atomic 30000 ms (100x) 2 threads lock 224000 ms (746x) Trisha Gee : Understanding the Disruptor (slideshare)
  13. Software Transactional Memory • Based on Database technology ◦ A)tomic

    ◦ C)onsistent ◦ I )solated ◦ D)urable • All writes are within a transaction ◦ idempotent ◦ Without side effects • Automatic retry on rollback • Used in some functional languages ◦ e.g. clojure Not necessary
  14. Clojure • Separation between reference and state ◦ State is

    immutable ◦ Only Identity Objects (ref) are writable • References can only be modified in a transaction Balance 100 200 Transacted
  15. Simple clojure example (def balance (ref 0)) (println "Balance is

    " @balance) (dosync (ref-set balance 100) ) (println "Balance is now " @balance)
  16. Write skew • Clojure transactions guard against multiple writes through

    transactions • Reads are usually outside of transaction ◦ Last committed value (def checking-account (ref 500)) (def savings-account (ref 600)) (defn withdraw-account [from-balance constraining-balance amount] (dosync (let [total-balance (+ @from-balance @constraining-balance)] (if (>= (- total-balance amount) 1000) (alter from-balance - amount) (println "Sorry can't withdraw due to insufficient funds") ) ) ) ) (future (withdraw-account checking-account savings-account 100)) (future (withdraw-account savings-account checking-account 100))
  17. STM in Java • Clojure is available directly in java

    ◦ import clojure.lang.Ref; • or implemented in diverse STM libraries ◦ e.g. Multiverse example
  18. STM Usage • Atomic writes with automatic retries ◦ Idempotent

    operations ◦ No side effects • Simple, guaranteed consistency • Best for scenarios with ◦ Few writes ◦ Many reads
  19. Actors paradigm • Message based communication between objects • An

    Actor ◦ Has its own message queue ◦ Execute actions sequentially • Calling an actor ◦ Is usually asynchronous ◦ Block for synchronous calls • All actors share a threadpool • Used extensively in Erlang and Scala ◦ Scala have multiple actor libraries ◦ Upcoming examples use akka libraries
  20. Scala Untyped Actor class HollywoodActor extends Actor { def receive

    = { case role => println "Playing " + role } } object HollywoodActor { def main(args: Array[String]) : Unit = { val johnnyDepp = Actor.actorOf[HollywoodActor].start() johnnyDepp ! "Jack Sparrow" johnnyDepp ! "Edward Scissorhands" johnnyDepp ! "Willy Wonka" Actors.registry.shutdownAll } }
  21. Scala Untyped Actor (synkron) class FortuneTeller extends Actor { def

    receive = { case name : String => self.reply_?(String.format("%s, your future is bright", name)) } } object FortuneTeller { def main(args: Array[String]) : Unit = { val nostradamus = Actor.actorOf[FortuneTeller].start() nostradamus ! "Niels" val response = nostradamus !! "Martin" response match { case Some(responseMessage) => println(responseMessage) case None => println ("Never got a response before timeout") } Actors.registry.shutdownAll } }
  22. Typed Actor Wrap instance as an actor All method calls

    are handled asynchronous Scala uses traits as type Provides an object with isolated mutability example
  23. Conclusion Scale your tasks for parallel execution • Java has

    become much better with regards to concurrency, but... ◦ STM provide a consistency advantage in large, read intensive data models ◦ Actors break traditional sequential processing and isolates execution via (primarily) asynchronous messages poolsize = number of cores / (1 - blocking coefficient)
  24. Questions and Answers "I refuse to answer that question on

    the grounds that I don't know the answer" "We have normality. I repeat we have normality. Anything you still cannot cope with is therefore your own problem" He was a dreamer, a thinker, a speculative philosopher..... or, as his wife would have it, an idiot For a moment, nothing happened. Then, after a second or two, nothing continued to happen