Design Patterns & Concurrency Sebastian Graf, Oliver Haase 1
Expectations ? ...on the concurrency-part... 2
Outline of the concurrency part I. Fundamentals II. Concurrent Applications III. Liveness, Performance and Hazards IV. Advanced T opics All mapped on object-oriented programming with Java. 3
Why? 4
Why? ‣ More responsive programs due to less blocking ‣ Exploiting multi-processor architectures ‣ T ask-oriented working (e.g. like in Servlets, RMI) ‣ Simply handling of asynchronous events 5
Threads are everywhere ‣ Garbage Collection ‣ RMI Invocation (marshalling / unmarshalling) ‣ Servlets Importance of thread-safety is crucial! 6
Threadsafe ? public class Sequence { private int value; /** Returns a unique value. */ public int getNext() { return value++; } } 7
Thread-Unsafe ! 8
Threadsafe Impl. @ThreadSafe public class Sequence { @GuardedBy("this") private int nextValue; public synchronized int getNext() { return nextValue++; } } 9
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. 10
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); } } 11
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); } } 12
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); } } 13
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); } } } 14
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); } } } 15
Poor concurrency 16
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 17
Amdahl's law in detail ‣ P: Parts to be parallized ‣ 1-P: Part not to be parallized ‣ N: Number of Threads 18
Scaling of Amdahl's Law 19
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); } } 20
Fix broken code ‣ Do not share state variables across threads ‣ Make sate variables immutable ‣ Synchronizing access to shared state variables 21
But... ‣ Do not serialize heavy computations ‣ Remember Amdahl's law ‣ Think about what needs to be parallized from the point of view of program correctness 22
Finally 23
Recommend
More recommend