Week 13 - The Java Memory Model and java.util.concurrent Foundations¶
Conceptual Core¶
The JMM defines what a thread can see of another thread's writes, and when. Without it, every multi-threaded program is undefined behavior. The model is built on happens-before - synchronization actions create happens-before edges; without an edge, no visibility guarantee.
Mechanical Detail¶
- Happens-before sources: program order within a thread;
synchronizedunlock → subsequent lock;volatilewrite → subsequent read;Thread.start→ first action in the new thread; thread termination →joinreturn; final-field freeze at constructor exit. volatile: visibility + ordering, not atomicity.volatile long/doublegot atomic in modern specs; pre-JMM they could tear.synchronizedblocks vsReentrantLock. The lock is nowLockSupport.park-backed and cooperates with virtual threads (Loom).java.util.concurrent:ConcurrentHashMap,CopyOnWriteArrayList,BlockingQueuefamily,Semaphore,CountDownLatch,CyclicBarrier,Phaser. Read the Javadoc; Doug Lea writes the best Javadoc in the language.- Atomics:
AtomicInteger/AtomicLong/AtomicReference. Pre-9:Atomic*FieldUpdater. Post-9:VarHandleis preferred. ThreadLocal- the perennial classloader-leak source in app servers. Avoid when possible; prefer scoped values (week 15).
Lab¶
Implement a single-producer single-consumer ring buffer two ways: with synchronized+wait/notify, and with VarHandle + acquire/release. JMH them. Run with -XX:+PrintAssembly (HotSpot debug build, or use the hsdis plugin on a normal build) and find the membar instructions.
Idiomatic Drill¶
Read JSR-133 (the JMM) cookbook. Re-read every volatile and synchronized use in a codebase you maintain.
Production Hardening Slice¶
Enable the race detector - wait, Java doesn't have one. The substitutes: ThreadSanitizer via the OpenJDK TSAN port (experimental), Lincheck (formal model-checker for concurrent classes), jcstress (Shipilëv's tool, for memory-model edge cases). Add a jcstress test for your ring buffer.