-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCopyOnWriteArrayListDemo.java
More file actions
149 lines (132 loc) · 5.39 KB
/
Copy pathCopyOnWriteArrayListDemo.java
File metadata and controls
149 lines (132 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package Phase5_CollectionsLambdasStreams.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* java.util.concurrent.CopyOnWriteArrayList<E>
* --------------------------------------------
* A thread-safe List variant where EVERY MUTATION COPIES the underlying
* array. Iteration walks the SNAPSHOT taken when the Iterator was created -
* it never sees subsequent writes and never throws
* ConcurrentModificationException.
* <p>
*
* Cost / Benefit
* --------------
* <p>
*
* Reads (get, iterator, contains): very FAST. No synchronization
* because the snapshot never changes.
* <p>
*
* Writes (add, set, remove): EXPENSIVE. Each one allocates a
* new internal array.
* <p>
*
* When To Use It
* --------------
* - READ-MOSTLY shared lists: event listeners, observer registrations,
* configuration snapshots, plugins.
* - When you want simple iteration with no locks and few writers.
* <p>
*
* When NOT To Use It
* ------------------
* - Write-heavy or large collections - the copy cost dominates.
* - Single-threaded code - ArrayList is much cheaper.
* <p>
*
* Snapshot Iteration Semantics
* ----------------------------
* List = [A, B, C]
* it = list.iterator(); // captures snapshot [A, B, C]
* list.add(D); // current state becomes [A, B, C, D]
* it.next() // sees A
* it.next() // sees B
* it.next() // sees C
* it.hasNext() // false - the iterator's snapshot ended
* <p>
*
* Iterator also DOES NOT SUPPORT remove()/set()/add() - the snapshot is
* immutable.
* <p>
*
* Specific Methods
* ----------------
* addIfAbsent(e) - add only if not already present
* addAllAbsent(c) - bulk addIfAbsent
* These two are useful for "set-like" registrations.
* <p>
*
* Cousin Class
* ------------
* CopyOnWriteArraySet - the Set version
*/
public class CopyOnWriteArrayListDemo {
public static void main(String[] args) throws InterruptedException {
section("1) Basic usage - looks like ArrayList");
CopyOnWriteArrayList<String> a = new CopyOnWriteArrayList<>();
a.add("alpha");
a.add("beta");
a.addIfAbsent("alpha"); // no-op (already present)
a.addIfAbsent("gamma"); // added
System.out.println("list = " + a);
section("2) Snapshot iterator semantics");
CopyOnWriteArrayList<Integer> live = new CopyOnWriteArrayList<>(List.of(1, 2, 3));
Iterator<Integer> snapshot = live.iterator(); // snapshot taken NOW
live.add(99);
live.add(100);
System.out.print("iterator sees: ");
while (snapshot.hasNext()) System.out.print(snapshot.next() + " ");
System.out.println();
System.out.println("list is now : " + live);
section("3) Iterator does NOT support remove()");
try {
live.iterator().remove();
} catch (UnsupportedOperationException e) {
System.out.println("iterator.remove() -> UnsupportedOperationException");
}
section("4) The textbook use case - thread-safe listener list");
CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
Subject subject = new Subject(listeners);
listeners.add(msg -> System.out.println(" listener-A got: " + msg));
listeners.add(msg -> System.out.println(" listener-B got: " + msg));
// Many concurrent broadcasts and ONE registration in the middle;
// none of the publishers will get a ConcurrentModificationException.
Thread publisher = new Thread(() -> {
for (int i = 0; i < 5; i++) subject.publish("evt-" + i);
});
Thread joiner = new Thread(() ->
listeners.add(msg -> System.out.println(" listener-C (late) got: " + msg)));
publisher.start();
joiner.start();
publisher.join();
joiner.join();
section("5) Writes are EXPENSIVE - rough timing");
final int N = 50_000;
CopyOnWriteArrayList<Integer> cow = new CopyOnWriteArrayList<>();
long t = System.nanoTime();
for (int i = 0; i < N; i++) cow.add(i);
long ms = (System.nanoTime() - t) / 1_000_000;
System.out.println("COW add " + N + " items: " + ms + " ms");
java.util.ArrayList<Integer> al = new java.util.ArrayList<>();
t = System.nanoTime();
for (int i = 0; i < N; i++) al.add(i);
long alMs = (System.nanoTime() - t) / 1_000_000;
System.out.println("ArrayList add " + N + " items: " + alMs + " ms");
// OUTPUT (timings vary; the relative ordering tells the story)
}
@FunctionalInterface
interface Listener { void onMessage(String msg); }
static class Subject {
private final List<Listener> listeners;
Subject(List<Listener> listeners) { this.listeners = listeners; }
void publish(String msg) {
// No synchronization needed: the iterator is a snapshot.
for (Listener l : listeners) l.onMessage(msg);
}
}
private static void section(String title) {
System.out.println("\n====== " + title + " ======");
}
}