atomics: Java Atomics and VarHandle companion code

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
This commit is contained in:
2026-09-30 06:23:03 +00:00
co-authored by Claude Sonnet 5
parent 32b8067ecf
commit ff183da163
21 changed files with 845 additions and 0 deletions
@@ -0,0 +1,59 @@
$ java -jar target/benchmarks.jar IncrementBenchmark -t <N> -rf text
(one JVM invocation per thread count; -t sets the JMH thread count for that run)
=== threads=1 ===
Benchmark Mode Cnt Score Error Units
IncrementBenchmark.atomicLong thrpt 5 152210.819 ± 13994.910 ops/ms
IncrementBenchmark.longAdder thrpt 5 85474.018 ± 4259.652 ops/ms
IncrementBenchmark.reentrantLock thrpt 5 52749.683 ± 9224.450 ops/ms
IncrementBenchmark.synchronized_ thrpt 5 41962.389 ± 5710.162 ops/ms
IncrementBenchmark.varHandleCas thrpt 5 92813.051 ± 18134.407 ops/ms
=== threads=2 ===
Benchmark Mode Cnt Score Error Units
IncrementBenchmark.atomicLong thrpt 5 55290.174 ± 5756.818 ops/ms
IncrementBenchmark.longAdder thrpt 5 175504.101 ± 19226.430 ops/ms
IncrementBenchmark.reentrantLock thrpt 5 12132.831 ± 3380.323 ops/ms
IncrementBenchmark.synchronized_ thrpt 5 7781.210 ± 5925.389 ops/ms
IncrementBenchmark.varHandleCas thrpt 5 31955.213 ± 7578.028 ops/ms
=== threads=4 ===
Benchmark Mode Cnt Score Error Units
IncrementBenchmark.atomicLong thrpt 5 58192.557 ± 1874.113 ops/ms
IncrementBenchmark.longAdder thrpt 5 176176.145 ± 10500.741 ops/ms
IncrementBenchmark.reentrantLock thrpt 5 49474.377 ± 6131.270 ops/ms
IncrementBenchmark.synchronized_ thrpt 5 8198.887 ± 18413.808 ops/ms
IncrementBenchmark.varHandleCas thrpt 5 32807.244 ± 14075.306 ops/ms
=== threads=8 ===
Benchmark Mode Cnt Score Error Units
IncrementBenchmark.atomicLong thrpt 5 58036.390 ± 10538.772 ops/ms
IncrementBenchmark.longAdder thrpt 5 176823.593 ± 19520.176 ops/ms
IncrementBenchmark.reentrantLock thrpt 5 49528.714 ± 9256.971 ops/ms
IncrementBenchmark.synchronized_ thrpt 5 8933.358 ± 7612.678 ops/ms
IncrementBenchmark.varHandleCas thrpt 5 31671.036 ± 9740.912 ops/ms
=== threads=16 ===
Benchmark Mode Cnt Score Error Units
IncrementBenchmark.atomicLong thrpt 5 55587.800 ± 3218.928 ops/ms
IncrementBenchmark.longAdder thrpt 5 171534.976 ± 37921.512 ops/ms
IncrementBenchmark.reentrantLock thrpt 5 48603.146 ± 6585.079 ops/ms
IncrementBenchmark.synchronized_ thrpt 5 8839.293 ± 3658.010 ops/ms
IncrementBenchmark.varHandleCas thrpt 5 31809.007 ± 7320.695 ops/ms
=== threads=32 ===
Benchmark Mode Cnt Score Error Units
IncrementBenchmark.atomicLong thrpt 5 56397.645 ± 6961.852 ops/ms
IncrementBenchmark.longAdder thrpt 5 162883.357 ± 14305.826 ops/ms
IncrementBenchmark.reentrantLock thrpt 5 43556.152 ± 10557.939 ops/ms
IncrementBenchmark.synchronized_ thrpt 5 9159.280 ± 8780.101 ops/ms
IncrementBenchmark.varHandleCas thrpt 5 32833.760 ± 2661.430 ops/ms
=== threads=64 ===
Benchmark Mode Cnt Score Error Units
IncrementBenchmark.atomicLong thrpt 5 43448.887 ± 5607.031 ops/ms
IncrementBenchmark.longAdder thrpt 5 166468.689 ± 22263.886 ops/ms
IncrementBenchmark.reentrantLock thrpt 5 44277.427 ± 6928.188 ops/ms
IncrementBenchmark.synchronized_ thrpt 5 10784.283 ± 3760.196 ops/ms
IncrementBenchmark.varHandleCas thrpt 5 29751.529 ± 1620.459 ops/ms
+21
View File
@@ -0,0 +1,21 @@
$ 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)
@@ -0,0 +1,7 @@
$ java -cp target/classes com.ankurm.atomics.VarHandleAccessModesDemo
plain set(1) / get() -> 1
setOpaque(2) / getOpaque() -> 2
setRelease(3) / getAcquire() -> 3
setVolatile(4) / getVolatile() -> 4
compareAndSet(4, 5) succeeded -> true, second compareAndSet(4, 6) succeeded -> false (expected false - value is 5, not 4, by the second call)
@@ -0,0 +1,4 @@
-------------------------------------------------------------------------------
Test set: com.ankurm.atomics.CounterCorrectnessTest
-------------------------------------------------------------------------------
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.221 s -- in com.ankurm.atomics.CounterCorrectnessTest