cow: CopyOnWriteArrayList - deterministic iterator-snapshot and synchronizedList CME demos, JMH read/write throughput, and the addLast/removeFirst fix for a real concurrent-writer race

This commit is contained in:
Claude
2026-10-01 12:09:44 +00:00
parent 2eaa464bad
commit 434054f246
17 changed files with 709 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
list before any iterator is taken = [Apple, Banana, Cherry]
beforeWrite.next() = Apple
[writer] added Date and Elderberry - list is now [Apple, Banana, Cherry, Date, Elderberry]
=== beforeWrite iterator was created BEFORE the write - draining the rest of it ===
beforeWrite.next() = Banana
beforeWrite.next() = Cherry
beforeWrite never saw Date or Elderberry, and threw no exception.
=== afterWrite iterator is created AFTER the write - it sees the current array ===
afterWrite.next() = Apple
afterWrite.next() = Banana
afterWrite.next() = Cherry
afterWrite.next() = Date
afterWrite.next() = Elderberry
Final list = [Apple, Banana, Cherry, Date, Elderberry]
@@ -0,0 +1,11 @@
[reader] it.next() = Apple
[writer] syncList.add("Date") completed - list is now [Apple, Banana, Cherry, Date]
[reader] it.next() threw ConcurrentModificationException, as expected
Exception actually thrown: java.util.ConcurrentModificationException
=== The fix: synchronize the WHOLE iteration yourself, as the Javadoc instructs ===
holding the lock for the whole iteration - starting safeWriter now
iterated 3 elements with no exception, because safeWriter
cannot acquire the same monitor until this synchronized block exits
after the block: syncList2 = [Apple, Banana, Cherry, Date]
+4
View File
@@ -0,0 +1,4 @@
-------------------------------------------------------------------------------
Test set: com.ankurm.cow.CowClaimsTest
-------------------------------------------------------------------------------
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.088 s -- in com.ankurm.cow.CowClaimsTest
+5
View File
@@ -0,0 +1,5 @@
Benchmark (container) (writer) Mode Cnt Score Error Units
ReadThroughputBenchmark.readSum COW NONE thrpt 3 1890.457 ± 486.709 ops/ms
ReadThroughputBenchmark.readSum COW BACKGROUND thrpt 3 911.480 ± 70.801 ops/ms
ReadThroughputBenchmark.readSum SYNC NONE thrpt 3 10.311 ± 23.312 ops/ms
ReadThroughputBenchmark.readSum SYNC BACKGROUND thrpt 3 9.428 ± 12.790 ops/ms
+9
View File
@@ -0,0 +1,9 @@
Benchmark (container) (size) Mode Cnt Score Error Units
WriteThroughputBenchmark.addRemove_fourConcurrentWriters COW 10 thrpt 3 2442.460 ± 10099.500 ops/ms
WriteThroughputBenchmark.addRemove_fourConcurrentWriters COW 1000 thrpt 3 404.113 ± 92.677 ops/ms
WriteThroughputBenchmark.addRemove_fourConcurrentWriters SYNC 10 thrpt 3 4256.370 ± 11472.184 ops/ms
WriteThroughputBenchmark.addRemove_fourConcurrentWriters SYNC 1000 thrpt 3 2431.519 ± 10671.130 ops/ms
WriteThroughputBenchmark.addRemove_oneWriter COW 10 thrpt 3 8590.710 ± 3673.510 ops/ms
WriteThroughputBenchmark.addRemove_oneWriter COW 1000 thrpt 3 692.991 ± 1173.810 ops/ms
WriteThroughputBenchmark.addRemove_oneWriter SYNC 10 thrpt 3 17149.052 ± 26734.246 ops/ms
WriteThroughputBenchmark.addRemove_oneWriter SYNC 1000 thrpt 3 11819.476 ± 794.382 ops/ms
+31
View File
@@ -0,0 +1,31 @@
# Warmup Iteration 1: <failure>
java.lang.ArrayIndexOutOfBoundsException: Index 12 out of bounds for length 12
at java.base/java.util.concurrent.CopyOnWriteArrayList.elementAt(CopyOnWriteArrayList.java:397)
at java.base/java.util.concurrent.CopyOnWriteArrayList.remove(CopyOnWriteArrayList.java:538)
at com.ankurm.cow.WriteThroughputBenchmark.addRemove_fourConcurrentWriters(WriteThroughputBenchmark.java:68)
at com.ankurm.cow.jmh_generated.WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.addRemove_fourConcurrentWriters_thrpt_jmhStub(WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.java:147)
at com.ankurm.cow.jmh_generated.WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.addRemove_fourConcurrentWriters_Throughput(WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.java:86)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:527)
at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:504)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
java.lang.IndexOutOfBoundsException: Index 1002 out of bounds for length 1002
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
at java.base/java.util.Objects.checkIndex(Objects.java:365)
at java.base/java.util.ArrayList.remove(ArrayList.java:552)
at java.base/java.util.Collections$SynchronizedList.remove(Collections.java:2753)
at com.ankurm.cow.WriteThroughputBenchmark.addRemove_fourConcurrentWriters(WriteThroughputBenchmark.java:68)
at com.ankurm.cow.jmh_generated.WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.addRemove_fourConcurrentWriters_thrpt_jmhStub(WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.java:147)
at com.ankurm.cow.jmh_generated.WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.addRemove_fourConcurrentWriters_Throughput(WriteThroughputBenchmark_addRemove_fourConcurrentWriters_jmhTest.java:86)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:527)
at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:504)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)