Sebastian Graf, Oliver Haase
1
Design Patterns &
Concurrency
2
Expectations ?
...on the concurrency-part...
3
Outline
of the concurrency part
I. Fundamentals
II. Concurrent Applications
III. Liveness, Performance and Hazards IV. Advanced Topics
All mapped on object-oriented programming with Java.
4
Why?
5
Why?
‣
More responsive programs due to less blocking‣
Exploiting multi-processor architectures‣
Task-oriented working (e.g. like in Servlets, RMI)‣
Simply handling of asynchronous events6
Threads are everywhere
‣
Garbage Collection‣
RMI Invocation (marshalling / unmarshalling)‣
ServletsImportance of thread-safety is crucial!
7
Threadsafe ?
public class Sequence { private int value;
/** Returns a unique value. */
public int getNext() { return value++;
} }
8
Thread-Unsafe !
9
Threadsafe Impl.
@ThreadSafe
public class Sequence {
@GuardedBy("this") private int nextValue;
public synchronized int getNext() { return nextValue++;
} }
10
Definition of Threadsafety
Managing access to state, in particular to shared, mutable state (directly to member- variables of one class) with
‣
Atomic change of state‣
Invariants, Pre- /Postconditions‣
…Providing any necessary synchronization so that the client needs no own one.
11
Simple Example
public class StatelessFactorizer implements Servlet { public void service(ServletRequest req,
ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
} }
12
State variable example
public class CountingFactorizer implements Servlet { private long count = 0;
public long getCount() { return count; }
public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
++count;
encodeIntoResponse(resp, factors);
}
}
13
Thread-safe state variable example
public class CountingFactorizer implements Servlet {
private final AtomicLong count = new AtomicLong(0);
public long getCount() { return count.get(); }
public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
count.incrementAndGet();
encodeIntoResponse(resp, factors);
} }
14
Next one...
public class UnsafeCachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastNumber = new AtomicReference<BigInteger>();
private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>();
public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber.get()))
encodeIntoResponse(resp, lastFactors.get() );
else {
BigInteger[] factors = factor(i);
lastNumber.set(i);
lastFactors.set(factors);
encodeIntoResponse(resp, factors);
} } }
15
One Solution...
public class SynchronizedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
public synchronized void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber))
encodeIntoResponse(resp, lastFactors);
else {
BigInteger[] factors = factor(i);
lastNumber = i;
lastFactors = factors;
encodeIntoResponse(resp, factors);
} } }
16
Poor concurrency
17
Excursion, Amdahl's Law
“For the past thirty years, computer
performance has been driven by Moore’s Law;
from now on, it will be driven by Amdahl’s Law.
[...]”
-- Doron Rajwan, Research Scientist, Intel Corp
18
Amdahl's law in detail
‣
P: Parts to be parallized‣
1-P: Part not to be parallized‣
N: Number of Threads19
Scaling of Amdahl's Law
20
One better solution
public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
@GuardedBy("this") private long hits;
@GuardedBy("this") private long cacheHits;
public synchronized long getHits() { return hits; } public synchronized double getCacheHitRatio() { return (double) cacheHits / (double) hits; }
public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req);
BigInteger[] factors = null;
synchronized (this) { ++hits;
if (i.equals(lastNumber)) { ++cacheHits;
factors = lastFactors.clone();
} }
if (factors == null) { factors = factor(i);
synchronized (this) { lastNumber = i;
lastFactors = factors.clone();
} }
encodeIntoResponse(resp, factors);
} }
21
Fix broken code
‣
Do not share state variables across threads‣
Make sate variables immutable‣
Synchronizing access to shared state variables22
But...
‣
Do not serialize heavy computations‣
Remember Amdahl's law‣
Think about what needs to be parallized from the point of view of program correctness23