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