-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWaitNotifyNotifyAll.java
More file actions
176 lines (162 loc) · 5.79 KB
/
Copy pathWaitNotifyNotifyAll.java
File metadata and controls
176 lines (162 loc) · 5.79 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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package Phase7_Concurrency.Multithreading;
/**
* Object.wait / notify / notifyAll
* --------------------------------
* The original Java inter-thread signalling primitives. They are
* INSTANCE methods on `Object` — every object's intrinsic monitor lock
* doubles as a condition variable.
* <p>
*
* Rules
* -----
* 1. You MUST own the monitor lock when calling these methods. Always
* inside a `synchronized` block on the same object you're waiting on.
* 2. wait() RELEASES the monitor while suspended; notify() wakes one
* waiter, notifyAll() wakes all of them. The woken thread then has
* to re-acquire the monitor before returning from wait().
* 3. Always loop the predicate:
* <p>
*
* while (!ready) lock.wait();
* <p>
*
* Spurious wakeups exist; predicates can also change between
* notify and the wait return.
* <p>
*
* The three methods
* -----------------
* wait() - wait indefinitely
* wait(long ms) - wait up to ms milliseconds
* wait(long ms, int nanos)
* <p>
*
* notify() - wake ONE waiter (which one is undefined)
* notifyAll() - wake ALL waiters
* <p>
*
* notify vs notifyAll
* -------------------
* - Use notifyAll when waiters may be waiting on DIFFERENT predicates
* of the same object — only the threads whose predicate is now true
* will exit; the others go back to waiting.
* - notify is a micro-optimisation for one-predicate cases. It is
* easy to introduce missed-signal bugs if you reach for it without
* thinking through every waiter.
* <p>
*
* Modern alternative: Lock + Condition
* ------------------------------------
* Lock lock = new ReentrantLock();
* Condition cond = lock.newCondition();
* <p>
*
* lock.lock();
* try {
* while (!ready) cond.await();
* ...
* } finally { lock.unlock(); }
* <p>
*
* Conditions let you have MULTIPLE wait-sets per lock (e.g. notFull /
* notEmpty in a bounded buffer).
*/
public class WaitNotifyNotifyAll {
private static final Object SIGNAL = new Object();
private static boolean ready = false;
public static void main(String[] args) throws InterruptedException {
section("1) wait / notify — single waiter");
ready = false;
Thread waiter = new Thread(() -> {
synchronized (SIGNAL) {
while (!ready) {
try { SIGNAL.wait(); } catch (InterruptedException ignored) {}
}
System.out.println("waiter saw ready");
}
}, "waiter");
waiter.start();
Thread.sleep(50);
synchronized (SIGNAL) {
ready = true;
SIGNAL.notify();
}
waiter.join();
section("2) notify vs notifyAll — multiple waiters, single predicate");
ready = false;
Thread[] waiters = new Thread[3];
for (int i = 0; i < 3; i++) {
final int id = i;
waiters[i] = new Thread(() -> {
synchronized (SIGNAL) {
while (!ready) {
try { SIGNAL.wait(); } catch (InterruptedException ignored) {}
}
System.out.println(" waiter " + id + " released");
}
}, "w-" + i);
waiters[i].start();
}
Thread.sleep(50);
synchronized (SIGNAL) {
ready = true;
SIGNAL.notifyAll(); // wake them all
}
for (Thread t : waiters) t.join();
section("3) Bounded buffer using wait/notifyAll (mini producer/consumer)");
Buffer<Integer> buf = new Buffer<>(2);
Thread prod = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try { buf.put(i); }
catch (InterruptedException ignored) {}
System.out.println(" produced " + i + ", size=" + buf.size());
}
}, "prod");
Thread cons = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try {
int v = buf.take();
System.out.println(" consumed " + v + ", size=" + buf.size());
Thread.sleep(40);
} catch (InterruptedException ignored) {}
}
}, "cons");
prod.start(); cons.start();
prod.join(); cons.join();
section("4) wait without holding the lock = IllegalMonitorStateException");
try {
SIGNAL.wait();
} catch (IllegalMonitorStateException e) {
System.out.println("expected: " + e);
}
section("done");
}
/** Hand-built bounded buffer using wait/notifyAll. */
static class Buffer<E> {
private final Object[] items;
private int count, head, tail;
Buffer(int cap) { this.items = new Object[cap]; }
synchronized void put(E e) throws InterruptedException {
while (count == items.length) wait(); // wait until NOT full
items[tail] = e;
tail = (tail + 1) % items.length;
count++;
notifyAll(); // wake potential takers
}
@SuppressWarnings("unchecked")
synchronized E take() throws InterruptedException {
while (count == 0) wait(); // wait until NOT empty
E e = (E) items[head];
items[head] = null;
head = (head + 1) % items.length;
count--;
notifyAll(); // wake potential putters
return e;
}
synchronized int size() { return count; }
}
private static void section(String title) {
System.out.println();
System.out.println("=== " + title + " ===");
}
}