Concurrency
Session - 2
Recap
Risks of Threads
- Safety Hazards
- race conditions
- Liveness Hazards
- deadlocks
- Performance Hazards
- context switching
- CPU - more time spend scheduling threads than running them
- context switching
- A class is thread‐safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code.
- If an object is correctly implemented, no sequence of operations ‐ calls to public methods and reads or writes of public fields ‐ should be able to violate any of its invariants or post‐conditions. No set of operations performed sequentially or concurrently on instances of a thread‐safe class can cause an instance to be in an invalid state.
THREAD SAFETY vague definition
Statelessness & Thread Safety ?
Stateless objects - always threadsafe
Atomicity
example: read-modify-write
@NotThreadSafe
public class UnsafeCountingFactorizer 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);
}
}A race condition occurs when the correctness of a computation depends on the relative timing or interleaving of multiple threads by the runtime; in other words, when getting the right answer relies on lucky timing.
Atomicity
example: check-then-act
@NotThreadSafe
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
} }We refer collectively to check‐then‐act and read‐modify‐write sequences as compound actions: sequences of operations that must be executed atomically in order to remain thread‐safe. In the next section, we'll consider locking, Java's built‐in mechanism for ensuring atomicity.
@ThreadSafe
public class MovingAverageCounter {
private final AtomicLong count = new AtomicLong(0);
private final AtomicLong sum = new AtomicLong(0);
public void addValue(long value) {
count.incrementAndGet();
sum.addAndGet(value);
}
public double getAverage() {
long currentCount = count.get();
return currentCount > 0 ?
(double)sum.get() / currentCount : 0.0;
}
}@ThreadSafe
public class AtomicMovingAverage {
private static class Snapshot {
final long count;
final long sum;
Snapshot(long count, long sum) {
this.count = count;
this.sum = sum;
}
}
private final AtomicReference<Snapshot> state =
new AtomicReference<>(new Snapshot(0, 0));
public void addValue(long value) {
while (true) {
Snapshot current = state.get();
Snapshot next = new Snapshot(
current.count + 1,
current.sum + value);
if (state.compareAndSet(current, next)) {
break;
}
}
}
public double getAverage() {
Snapshot current = state.get();
return current.count > 0 ?
(double)current.sum / current.count : 0.0;
}
}ConcurrencySession - 2
By Kaushik Rishi
ConcurrencySession - 2
- 36