Step 1 of 7 · Reading · ~4 min
Learn
Concurrency
Threads and the Memory Model
In Java Intermediate you started threads, joined them, and fixed a lost-update race with AtomicInteger. This lesson explains why those fixes work, and teaches the tool you have not used yet: making one thread wait until another has changed something.
A write is not automatically seen
Each core may keep a variable in a register or cache. Without a rule saying otherwise, the JVM is free to let a reader keep using a stale copy. This program can hang forever:
static boolean ready = false; // plain field
static int config = 0;
// thread A // thread B
config = 42; while (!ready) { } // spins
ready = true; System.out.println(config);
The JIT may read ready once and reuse it, so B never leaves the loop. Even if it does, it could print 0, because nothing forces config = 42 to become visible before ready = true. Declaring ready as volatile fixes both problems: B's loop sees the new value, and every write A made before the volatile write is visible to B after the volatile read.
Happens-before: the only promise you get
The Java Memory Model does not talk about "latest value". It defines happens-before edges; if action X happens-before action Y, then Y sees everything X wrote. The edges you will use:
- a write to a
volatilefield, then a later read of that same field - releasing a monitor (leaving
synchronized), then a later acquire of the same monitor Thread.start(), then everything the new thread does- everything a thread does, then
join()returning in another thread
No edge, no guarantee. That is why a result array filled by a worker can be read safely after join() without any extra locking.
Atomic does not mean visible, and visible does not mean atomic
volatile int n; n++ is still read, add, write: two threads can both read 7 and both write 8. For a read-modify-write you need either a lock or a compare-and-set (CAS) loop: read the current value, compute the new one, and store it only if nobody changed the field in between; otherwise retry.
static final AtomicInteger sold = new AtomicInteger();
static boolean reserve(int limit) { // ticket office with a hard limit
while (true) {
int cur = sold.get();
if (cur >= limit) return false;
if (sold.compareAndSet(cur, cur + 1)) return true; // else someone beat us; retry
}
}
Waiting for a condition: wait and notifyAll
Spinning on a flag burns a core. A thread that needs a condition to become true should sleep on a monitor and be woken when the condition may have changed:
class Latch {
private boolean open = false;
synchronized void await() throws InterruptedException {
while (!open) wait(); // releases the monitor while asleep
}
synchronized void release() {
open = true;
notifyAll(); // wake every sleeper so it re-checks
}
}
Three rules make this correct. wait, notify and notifyAll must be called while holding the object's monitor, or you get IllegalMonitorStateException. The test sits in a while, never an if, because a woken thread must re-check: another thread may have run first, and spurious wakeups exist. And when threads wait for different conditions on the same monitor, use notifyAll; a single notify can wake the wrong one and leave the right one asleep forever.
Common mistakes
- Making a counter
volatileand callingn++. Updates are lost and the total comes out below the expected one. if (!open) wait();A thread that wakes with the condition still false carries on and reads garbage.notify()where producers and consumers share one monitor. The wakeup can go to a thread of the wrong kind, and the program hangs with every thread waiting.- Calling
wait()outsidesynchronized. It throwsIllegalMonitorStateExceptionimmediately.
Your exercise
Finish a fixed-capacity buffer shared by one producer thread and one consumer thread: put must block while it is full and take must block while it is empty. The storage and the two threads are already written; the most likely mistake is a method that works on a quiet run but never wakes its partner, so the program hangs.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…