AtomicLong/LongAdder/VarHandle counters benchmarked against the synchronized and ReentrantLock baselines from the locks module, a deterministic ABA race against a hand-rolled Treiber stack plus the AtomicStampedReference fix, and a VarHandle access-modes demo (plain/opaque/acquire-release/volatile). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01FhzLY5p6okFva3qsnsRyvM
22 lines
1.3 KiB
Plaintext
22 lines
1.3 KiB
Plaintext
$ java -cp target/classes com.ankurm.atomics.AbaProblemDemo
|
|
|
|
=== Part 1: a real ABA race against TreiberStack ===
|
|
Initial stack (top first): [A, B, C]
|
|
Main thread popped, legitimately: "A", then "B"
|
|
Stack after those two real pops: [C]
|
|
Main thread pushed the SAME "A" node object back: [A, C]
|
|
Thread 1's stale CAS result: CAS succeeded, pop() would have returned "A"
|
|
Stack contents after Thread 1's stale CAS: [B, C]
|
|
"B" is back in the stack even though the main thread already popped it and
|
|
nobody ever pushed it again - Thread 1's CAS matched on reference identity
|
|
alone (top was "A" both times it looked) and blindly installed a newTop
|
|
("B") that was computed from a read that happened before two pops and a
|
|
push it never saw. "A" was also just handed out twice: once to the main
|
|
thread's first pop(), once to Thread 1's stale one.
|
|
|
|
=== Part 2: AtomicStampedReference detects the same shape of race ===
|
|
Reader captured: ref="A", stamp=0
|
|
After a concurrent A -> B -> A round trip: ref="A", stamp=2 (reference is back to "A", but the stamp moved on)
|
|
A plain AtomicReference.compareAndSet("A", "Z") would see reference == "A" and succeed: true
|
|
AtomicStampedReference.compareAndSet("A", "Z", 0, 1) actually succeeded: false (false is correct - the stamp proves a change happened in between, even though the reference alone looks unchanged)
|