Concurrent Programming in Java

  • 351 views
Uploaded on

Concurrent Programming in Java …

Concurrent Programming in Java
Threads, Executors, ForkJoin, CompletableFuture, RXJava, Actors

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
351
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
23
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • The speedup of a program using multiple processors in parallel computing is limited by the sequential fraction of the program. For example, if 95% of the program can be parallelized, the theoretical maximum speedup using parallel computing would be 20× as shown in the diagram, no matter how many processors are used.

Transcript

  • 1. Concurrent Programming in Java
  • 2. Index ● Concurrency ● Threads ● Executors ● ForkJoin ● Paralell Streams ● CompletableFuture ● RXJava ● Actors
  • 3. Concurrency? „a property of systems in which several computations are executing simultaneously, and potentially interacting with each other“ http://en.wikipedia.org/wiki/Concurrency_(computer_science)
  • 4. Why Concurrency?
  • 5. Concurrency vs Paralellism Paralell Concurrent context switching
  • 6. Amdahl's Law The speedup of a program using multiple processors in parallel computing is limited by the sequential fraction of the program. For example, if 95% of the program can be parallelized, the theoretical maximum speedup using parallel computing would be 20× as shown in the diagram, no matter how many processors are used.
  • 7. Concurrency Paralellism no context switch context switch http://www.scanvine.com/source/daily-beasthttp://20minutosderisa.blogspot.co.at/
  • 8. Java1.0 - Threads public class TestRunnable implements Runnable { public void run() { //do something } } Thread t = new Thread(new TestRunnable()); t.start(); //Thread starts t.join(1000); //Waits 1 second until thread finishes t.isAlive(); //checks if thread is alive t.interrupt(); // stops thread
  • 9. Communication betwen Threads ● Threads communicate by sharing access to fields ● This kind of communication can lead to: – Thread interference: sequence of steps overlap, leading to wrong results. – Memory consistency errors: changes to objects are not immediatelly visible to other threads
  • 10. Thread Interference – sequence of steps overlap, leading to wrong results
  • 11. Memory Consistency Errors //thread A increments counter counter++; // counter is shared by 2 threads int counter = 0; Thread A Thread B //thread B prints out counter, // right after A's change System.out.println(counter); Which value prints out B? there's no guarantee that thread A's change to counter will be visible to thread B, unless the programmer has established a happens-before relationship between these two statements
  • 12. Threads ● To prevent Thread Interference and Memory Consistency errors, we use synchronization, that can introduce Thread Contention – Deadlocks: threads wait for each other forever. – Starvation: thread blocks for a long time, because synchronized block takes too much time to execute. – Livelocks: threads introduce logic to avoid deadlocks, but they fail reapeatedly.
  • 13. Deadlock
  • 14. Thread Problems ● Expensive to create – CPU + RAM (around. 512Kb per Thread!) ● Performance overhead when CPU does context switching (switching between threads) – Everytime a thread blocks or sleeps, context is switched ● Memory is shared around threads – Problem with concurrent access to shared objects ● Difficult to control flow ● Difficult to control the number of threads started ● Locks to control access to shared resources – Can introduce thread contention
  • 15. Java5 – Concurrency Enhancements ● Atomic Numbers (AtomicInteger): get and increment a value atomically ● Concurrent Collections (e.g. ConcurrentHashMap): supports concurrent reads and updates ● ExecutorService ● Futures
  • 16. ExecutorService ● Takes care of executing (and controlling the execution) of asynchronous tasks ● Configurable Thread Pools – CorePoolSize – MaximumPoolSize, – KeepAliveTime – ThreadFactory – RejectedExecutionHandler ● Predefined Executors – Cached – Fixed – Scheduled – SingleThreaded http://blog.jessitron.com/2014/01/choosing-executorservice.html
  • 17. Executor Executor executor = new ThreadPoolExecutor(...) executor.execute(new Runnable() { public void run() { //execute task } }); ● Parent Interface of ExecutorService ● Just provides one method: execute
  • 18. ExecutorService //creates the executor service ExecutorService executorService = Executors.newFixedThreadPool(5); //submits a task to be executed asynchronously Future<String> future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "hi"; } }); //blocks until the result is ready String message = future.get(); // no more executions will be accepted executorService.shutdown(); // blocks until all tasks have completed execution executorService.awaitTermination(1, TimeUnit.SECONDS);
  • 19. ExecutorService + Callable + Future //submits a task to be executed asynchronously Future<String> future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "hi"; } }); //blocks until the result is ready String message = future.get(); Blocks until result is ready (thread has finished) ● Callable allows threads to return a result ● Future holds the result of the asynchronous computation
  • 20. Future methods ● get() – BLOCKS until result is ready ● get(timeout) throws TimeoutException ● cancel(mayInterruptIfRunning) – If task has not started, it will not be started – Otherwise, if mayInterruptIfRunning is true, it will Interrupt ● isDone() ● isCancelled()
  • 21. ExecutorService – more methods ● List<Future> invokeAll(Collection <Callable> Tasks – Executes all tasks, and returns when ALL have completed execution ● Object invokeAny(Collection<Callable>) – Starts execution of all tasks and returns the result of one finished task. Tasks that have not completed, are cancelled. ● invoke* and await* methods support timeout parameters. If task doesn't finish on time, method still returns
  • 22. ExecutorService - Pros ● Now we can: – Cache Threads => less overhead – Control Size of Pool => less risk of OOM – Easily retrieve values of asynchronous computation (Future) – Submit multiple tasks simultaneously
  • 23. Executors - Cons ● How to control multiple executions? Future<String> fA = executorService.submit(....); Future<String> fB = executorService.submit(....); Future<String> fC = executorService.submit(....); String a = fA.get(); String b = fB.get(); String c = fC.get(); What if fB and fC are ready before fA?
  • 24. Executors - Cons ● How to nest calls? Future<String> fA = executorService.submit(new RemoteCall()); Future<String> fB = executorService.submit(new RemoveCallWithParam(fA.get()); ….......... String a = fA.get(); String a = fB.get(); fA.get() BLOCKS!
  • 25. Callbacks executorService.execute(new RemoteCall(new Callback() { public void notify(Object param) { //remote call has finished – do something } })); //continue doing something interface Callback { void notify(Object param); } class RemoteCall implements Runnable { private Callback callback; public RemoteCall(Callback callback) { this.callback = callback; } public void run() { //execute remote call callback.notify(param) } } when RemoteCall finishes it notifies the registered callback no blocking
  • 26. Executors - Cons ● Futures block and are difficult to nest ● Callbacks do not block, but do not solve the problem of nested composition
  • 27. Java7 - ForkJoin ● Implementation of ExecutorService ● Aims to utilize a multi-core architecture at a maximum ● Uses a work-stealing algorithm: if a worker runs out of tasks, can steal work from others ● Useful for recursive tasks 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
  • 28. No Work-Stealing http://dantravelling.wordpress.com/2012/03/02/short-haul-in-europe-sk547-cph-ams/
  • 29. public class ForkSum extends RecursiveAction { private final int[] array; private final int start; private final int end; private final AtomicInteger result; public ForkSum(int[] array, int start, int end, AtomicInteger result) { super(); this.array = array; this.start = start; this.end = end; this.result = result; } private void computeDirectly() { int partialSum = 0; for (int i = start; i < end; i++) { partialSum += array[i]; } result.addAndGet(partialSum); } @Override protected void compute() { if ((start - end) < 10) { computeDirectly(); } else { int delta = (end - start) / 2; ForkSum a = new ForkSum(array, start, start + delta, result); ForkSum b = new ForkSum(array, start + delta + 1, end, result); invokeAll(a, b); } }
  • 30. int[] array = new int[2046]; Arrays.fill(array, 2); AtomicInteger result = new AtomicInteger(0); ForkSum forkSum = new ForkSum(array, 0, array.length, result); ForkJoinPool pool = new ForkJoinPool(); pool.invoke(forkSum); System.out.println(forkSum.getResult()); ForkJoinPool uses work stealing We can set the „parallelism level“ to ForkJoinPool (defaults to number of processors)
  • 31. Java7 - ForkJoin ● Only really useful for recursive tasks ● Does not help with the problems with had in the Executors (blocking & nested composition)
  • 32. Java8 – Streams // PROBLEM: Perform a time-intensive computation // to each element of the list List<Integer> col = Arrays.asList(1, 2, 3); // Procedural Solution List<Integer> colResult = new ArrayList<Integer>(); for (Integer i : col) { colResult.add(expensiveOperation(i)); } //Functional Solution: use map() with a HighOrderFunction List<Integer> colResultF = col.stream().map(i -> expensiveOperation(i)) .collect(Collectors.toList());
  • 33. Java8 – Parallel Streams //Functional Solution+: // parallelize the stream // and use map() with a HighOrderFunction List<Integer> colResultFP = col.parallelStream().map(i -> expensiveOperation(i)) .collect(Collectors.toList());
  • 34. Java8 - CompletableFuture ● Futures with – Callbacks – Transformations – Possibility to explicitly set a value in the Future (i.e. Promise)
  • 35. Completing a CompletableFuture //CALLEE public CompletableFuture<String> doSomething() { final CompletableFuture<String> future = createFuture(); // do something (synch or asynch) // when ready, call future.complete("hi"); // that will unlock the clients waiting for the future return future; } //CALLER CompletableFuture<String> cf = doSomething(); // Blocks until callee „completes“ the future with f.complete String result = cf.get(); will normally be executed asynch blocks
  • 36. How to get a value from a CompletableFuture ● get() – BLOCKS until the value is present, and returns it ● get(timeout, timeoutUnit) – BLOCKS until the value is present, or the timeout has been reached. If the later , a TimeoutException is thrown ● getNow(valueIfAbsent) – Doesn't block! if the value is present, it returns it; otherwise, the valueIfAbsent param (default) is returned
  • 37. How to complete a CompletableFuture ● complete(value) – completes ok with the value – only the first call takes effect – subsequent calls are ignored ● completeExceptionally(ex) – completes with exception – get() will throw an exception in the caller ● cancel() – completes with a CancellationException
  • 38. Completing a CompletableFuture ● Why is this useful? – We can execute a task sometimes in synchronous, and sometimes in asynchronous mode, without having to change the client. We just deliver a CompletableFuture, and the implementation decides the execution mode.
  • 39. Creating a CompletableFuture // <Java7 CompletableFuture<String> f = CompletableFuture.supplyAsync( new Supplier<String>() { @Override public String get() { // perform task return "hi"; } }, executorService); // OR Using Lambdas CompletableFuture<String> ft = CompletableFuture.supplyAsync( () -> "hi", executorService); Supplier is a functional interface Similar to Callable if no executor is provided, the ForkJoinPool.commonPool will be used
  • 40. Creating a CompletableFuture ● CompletableFuture<T> CompletableFuture.supplyAsync(supplier<T>, executor> – If no executor is provided, a Common Static pool for all tasks called ForkJoinPool.commonPool wil be used. This executor cannot be shutdown ● CompletableFuture<Void> CompletableFuture.runAsync(runnable, exec) – Accepts a Runnable instead of a Supplier → no value will be returned
  • 41. CompletableFuture Registering Callbacks ● .thenApply → registers a callback, that will be executed asynchronously when the future is ready. It uses the same thread that executed the future! ● .thenApplyAsynch → same as .thenApply, but the callback will be executed in a different thread than the one that completed the future map in fp terms
  • 42. CompletableFuture - Nesting and Composing CompletableFuture<Order> f1 = persistOrder(o); CompletableFuture<Order> f2 = f1.thenApply(o → transferOrderToContractor(o)); CompletableFuture<Order> f3 = f2.thenApply(o → updateSearchIndex(o)); CompletableFuture<Order> f4 = f3.thenApply(o → sendMonitoringInfo(o)); //OR, in a line: CompletableFuture<Order> f1 = persistOrder(o) .thenApply(o → transferOrderToContractor(o)) .thenApply(o → updateSearchIndex(o)) .thenApply(o → sendMonitoringInfo(o)); thenApply doesn't block! operations are executed in order, but do not block map in fp terms
  • 43. Set<Order> ordersToProcess = ….; ordersToProcess.stream().map(o → persistOrder(o) .thenApply(o → transferOrderToContractor(o)) .thenApply(o → updateSearchIndex(o)) .thenApply(o → sendMonitoringInfo(o))); CompletableFuture Multiple Items
  • 44. CompletableFuture<Order> f1 = persistOrder(o); CompletableFuture<Order> f2 = f1.thenApply(o → transferOrderToContractor(o)); f2.thenAccept(o → logger.debug(„Order „ + o + „ succesfully processed“); CompletableFuture Consuming Final Value ● thenAccept → accepts a function that will be executed when the CompletableFuture has finished
  • 45. CompletableFuture Error Handling ● exceptionally → accepts a function with the exception as parameter ● handle → accepts a function with both the normal result AND the exception as parameter, but only one of them is set CompletableFuture<Order> f1 = persistOrderAsynch(o); f1.exceptionally(e -> sendAlertMessage(e)); //OR f1.handle((order, ex) -> { if (ex != null) { sendAlertMessage(ex); return null; } else { logger.info("Order persisted: " + order); return order; } });
  • 46. CompletableFuture Composing ● CompletableFutures provides functions to combine multiple futures together ● thenCompose → same as thenApply, but the function that accepts returns a CompletableFuture private CompletableFuture<Status> sendData(String data) {…}; CompletableFuture<String> f1 = retrieveData(); CompletableFuture<Status> fs = f1.thenCompose(data → sendData(data)); flatMap in fp terms
  • 47. CompletableFuture Combining ● thenCombine → executes a callback when 2 completableFutures finish CompletableFuture<Address> f1 = retrieveContactAddress(contact); CompletableFuture<Identification> f2 = retrieveContactIdentification(contact); CompletableFuture<String> fs = f1.thenCombine(f2, (address, id) → peristContactData(address id));
  • 48. CompletableFuture Accepting one ● acceptEither → returns the future that completes first ● applyToEither → same than accept either, but it executes a callback when the first future completes CompletableFuture<Contact> f1 = searchContactInDB(name); CompletableFuture<Contact> f2 = searchContactInIndex(name); CompletableFuture<Contact> fs = f1.applyToEither(f2, c → sysout(„Contact found: „ + c); we do not care who finishes first. Just want one
  • 49. CompletableFuture Static Methods ● anyOf → static method, similar to acceptEither, that returns the future that completes first ● allOf → static method, that returns a future that will complete when all futures complete CompletableFuture<Contact> f1 = searchContactInDB(name); CompletableFuture<Contact> f2 = searchContactInIndex(name); CompletableFuture<Contact> fs = CompletableFuture.anyOf(f1, f2); we do not care who finishes first. Just want one
  • 50. CompletablFuture - Problems ● Transactions – In a EE App Server, we can use the ManagedExecutorService – Tasks submitted via ManagedExecutorService can get components @Injected (e.g. EntitiyManager) @Resource ManagedExecutorService managedExecutorService;
  • 51. RX Java ● Java Implementation of ReactiveExtensions (.NET) ● Library for composing asynchronous and event-based programs ● Introduces the Observable type (extension the Observer GOF Pattern) – consumer subscribes to an event, and producer pushes data when available ● Can be seen as an Iterator that, instead of pulling, gets the data pushed. ● Futures work on single items – Observables work on multiple items
  • 52. event Iterable (pull) Observable (push) retrieve data T next() onNext(T) discover error throws Exception onError(Exception) complete returns onCompleted() Iterable vs Observable An Observable is the asynchronous/push "dual" to the synchronous/pull Iterable
  • 53. 4 3 2 1…. PULL PUSH 4 3 2 1….
  • 54. Observable<String> homeMadeSyncObservable = Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("1"); subscriber.onNext("2"); subscriber.onNext("3"); subscriber.onCompleted(); } }); Creating Observables - Sync Observable<String> observable = Observable.from(Arrays.asList("1", "2", "3")); ● Observable.create • Observable.from
  • 55. Observable.from(numbers).subscribe(n -> System.out.println("value received: " + n), t -> System.out.println("Exception thrown: " + t), () -> System.out.println("observable completed") ); Subscribing to Observables ● subscribe function accepts three parameters in the form of functions – onNext → function to be called when a value is emitted. The value is passed as parameter to the function. – onError → function to be called when an exception is emitted. The exception is passed as parameter to the function – onComplete → function to be called when the observable doesn't have any more value to emit (has finished). This function doesn't accept any parameter.
  • 56. Holly Lambdas! Observable.from(numbers).subscribe(new Action1<Integer>() { @Override public void call(Integer n) { System.out.println("value received: " + n); } }, new Action1<Throwable>() { @Override public void call(Throwable t) { System.out.println("Exception thrown: " + t); } }, new Action0() { @Override public void call() { System.out.println("observable completed"); } }); Observable.from(numbers).subscribe(n -> System.out.println("value received: " + n), t -> System.out.println("Exception thrown: " + t), () -> System.out.println("observable completed") );Java8 Java7
  • 57. Subscribing to Observables ● Instead of passing three functions to subscribe, we can implement the Observer Interface. public interface Observer<T> { /** * Notifies the Observer that the {@link Observable} has finished sending push-based notifications. public abstract void onCompleted(); /** * Notifies the Observer that the {@link Observable} has experienced an error condition. */ public abstract void onError(Throwable e); /** * Provides the Observer with new data. */ public abstract void onNext(T t); }
  • 58. Subscribing to Observables ● or, if we are just interested in the values, we can just „listen“ to the emitted values by passing just the first function Observable<Integer> observable = Observable.from(1, 2, 3); observable.subscribe(t -> System.out.println(t));
  • 59. 4 3 2 1…. OBSERVABLE OBSERVER Observable.from(Arrays.asList("1", "2", "3")); obs.subscribe (n -> echo("value received: " + n), t -> echo("Exception thrown: " + t), () -> echo("observable completed") );
  • 60. Subscriptions ● Observable.subscribe() returns a Subscription object, that offers two methods: /** * Stop receiving notifications on the {@link Subscriber} that was registered when this Subscription was received. * <p> * This allows unregistering an {@link Subscriber} before it has finished receiving all events (ie. before onCompleted is called). */ public void unsubscribe(); public boolean isUnsubscribed(); ● If we unsubscribe, the Observer will stop receiving events
  • 61. Observable<String> homeMadeAsyncObservable = Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { final Thread thread = new Thread(() -> { subscriber.onNext("1"); subscriber.onNext("2"); subscriber.onNext("3"); subscriber.onCompleted(); }); thread.start(); } }); Creating Observables - Async ● Observable.create – Async with Thread
  • 62. final ExecutorService threadPool = Executors.newCachedThreadPool(); Observable<String> observableWithExecutor = Observable.create(new OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { threadPool.execute(() -> subscriber.onNext("a")); threadPool.execute(() -> subscriber.onNext("b")); threadPool.execute(() -> subscriber.onNext("c")); try { threadPool.shutdown(); threadPool.awaitTermination(2, TimeUnit.SECONDS); subscriber.onCompleted(); } catch (InterruptedException e) { subscriber.onError(e); } } }); Creating Observables - Async ● Observable.create – Async with Executors
  • 63. Transforming Observables ● map() and flatMap() → same as thenApply and thenCompose in CompletableFuture observable.map( n -> n + 1) → Observable(2, 3, 4) observable.flatMap( n -> Observable.from(n+1)) → Observable(2, 3, 4) ● groupBy, scan, pivot, buffer, window... Observable<Integer> observable = Observable.from(1, 2, 3);
  • 64. Filtering Observables List<String> names = Arrays.asList("Stefan", "Sebastian", "Ruben", "Robert", "Harald"); Observable.from(names).filter(s -> s.startsWith("S")); → Observable(„Stefan“, „Sebastian“) Observable.from(names).take(2)); → Observable(„Stefan“, „Sebastian“) takeLast, first, elementAt, sample, timeout, distinct, ofType, ...
  • 65. Combining Observables zip, join, paralellMerge, combineLatest...
  • 66. MUCH more on RXJava https://github.com/Netflix/RxJava/wiki/
  • 67. Functional Reactive Programming (Netflix) ● All return types are Observable. The service layer decides to: – return immediately with a value (i.e. value in cache) – or block until result available – or return immediately and perform execution asynchronously
  • 68. Actors ● originated in 1973 ● objects that encapsulate state and behaviour ● very lightweight concurrent entities ● process messages asynchronously using an event-driven receive loop ● they communicate only via messages ● raise the abstraction level to work with events flowing through the system
  • 69. Actors MESSAGE SENDER MAILBOX ACTOR BEHAVIOUR DISPATCHER
  • 70. Actors - Supervising Actors build „trees of Actors“, forming a hierarchical structure each actor has exactly one supervisor, which is the actor that created it tasks are split up and delegated until they become small enough to be handled in one piece
  • 71. Actors - Supervising „Parents“ are responsble for handling „children“'s failures The recursive structure then allows to handle failure at the right level different supervision strategies can be configured: resume, restart, stop, escalate
  • 72. Actors Location Transparent & Distributable If an actor communicates with another one inside the same JVM or remotelly is just a matter of configuration program just holds an Actor's reference, not the instance! this makes possible to deploy actors wherever you want (local, remote) all messages must be serializable
  • 73. Actors - Isolation Actors run completely isolated - the only way to communicate with them is by sending messages
  • 74. Actors – JVM Implementations http://akka.io/
  • 75. Akka ● many actors share one thread. one actor typically weights 300 bytes ● an actor only receives one message at a time – while processing the message, the actor is „thread safe“ (subsequent processing rule) ● router actor: Creates children, and forwards messages to the children (kind of load balancer) ● distributed by default: everything is dessigned to work on a distributed environment ● prefer inmutable messages
  • 76. Akka Persistence ● Command Sourcing: "At least once" guarantees – event is persisted before it enters the system (it's not even validated) ● Event Sourcing: "At most once" guarantees – event is persisted afterwards (first, the sate of the actor is changed, and then the event is persisted) - decouples event changes from side effects
  • 77. END