Recap
Risks of Threads
THREAD SAFETY vague definition
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;
}
}