Table of Contents
ToggleDifference Between wait and notify in Java
在多執行緒的環境中,多個執行緒可能會嘗試修改同一資源,不正確地管理執行緒會導致一致性的問題,使用 synchronized 來保護協調程式碼運行,調用 wait 方法時,調用執行緒停止執行,直到 notify 或 notifyAll 方法被其他某個執行緒調用, Difference Between notify and wait in Java ,提供這兩種方法的應用, 本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- multithreading
| +- thread
| +- DifferenceWaitNotifyTest.java
單元測試
Difference Between wait and notify 提供執行緒暫停、喚醒、拋出例外等操作。
worker
Difference Between wait and notify 建立 1 個物件,提供儲存跟讀取 2 個方法,當儲存執行緒調用 put 方法耗時 3 秒,完成任務時,會喚醒另一條讀取執行緒,因調用 take 方法而被暫停的執行緒,建立 2 條執行緒,分別調用 1 個物件儲存跟讀取的方法。
protected class Worker {
private volatile boolean done = false;
public Worker() {
}
public synchronized void put() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] put finished");
done = true;
notify();
}
public synchronized boolean take() throws InterruptedException {
while (!done) {
System.out.println("T[" + Thread.currentThread().getId() + "] take waiting");
wait();
}
return done;
}
}
@Test
public void worker() {
Worker worker = new Worker();
Thread threadA = new Thread(() -> {
String id = "A";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
worker.put();
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
String id = "B";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
boolean done = worker.take();
System.out.println(
"T[" + Thread.currentThread().getId() + "] worker: " + id + " finished, result: " + done);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
threadB.start();
threadA.start();
try {
threadA.join();
threadB.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
T[12] worker: B ready
T[11] worker: A ready
T[12] take waiting
T[11] put finished
T[11] worker: A finished
T[12] worker: B finished, result: true
interruptWorker
Difference Between wait and notify 建立 1 個物件,提供儲存跟讀取 2 個方法,當儲存執行緒,調用 put 方法耗時 3 秒,完成任務時,沒有喚醒另一條讀取執行緒,因調用 take 方法而被暫停的執行緒,建立 2 條執行緒,分別調用 1 個物件儲存跟讀取的方法,主執行緒等待 2 秒後,調用 interrupt 中斷讀取執行緒。
protected class InterruptWorker {
private volatile boolean done = false;
public InterruptWorker() {
}
public synchronized void put() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] put finished");
done = true;
// notify();
}
public synchronized boolean take() throws InterruptedException {
while (!done) {
System.out.println("T[" + Thread.currentThread().getId() + "] take waiting");
wait();
}
return done;
}
}
@Test
public void interruptWorker() {
InterruptWorker worker = new InterruptWorker();
Thread threadA = new Thread(() -> {
String id = "A";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
worker.put();
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
String id = "B";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
boolean done = worker.take();
System.out.println(
"T[" + Thread.currentThread().getId() + "] worker: " + id + " finished, result: " + done);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
threadB.start();
threadA.start();
try {
threadA.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
long startTime = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - startTime > 1000) {
threadB.interrupt();
break;
}
}
}
T[12] worker: B ready
T[12] take waiting
T[11] worker: A ready
T[11] put finished
T[11] worker: A finished
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at org.ruoxue.java_147.multithreading.DifferenceWaitNotifyTest$InterruptWorker.take(DifferenceWaitNotifyTest.java:92)
illegalMonitorWorker
Difference Between wait and notify 建立 1 個物件,提供儲存跟讀取 2 個方法,當儲存執行緒使用另一個物件鎖與 notify 為不同的鎖時,會拋出例外,結束任務。
protected class IllegalMonitorWorker {
private volatile boolean done = false;
private final Object lock = new Object();
public IllegalMonitorWorker() {
}
public void put() throws InterruptedException {
synchronized (lock) {
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] put finished");
done = true;
notify();
}
}
public boolean take() throws InterruptedException {
synchronized (lock) {
while (!done) {
System.out.println("T[" + Thread.currentThread().getId() + "] take waiting");
wait();
}
}
return done;
}
}
@Test
public void illegalMonitorWorker() {
IllegalMonitorWorker worker = new IllegalMonitorWorker();
Thread threadA = new Thread(() -> {
String id = "A";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
worker.put();
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
String id = "B";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
boolean done = worker.take();
System.out.println(
"T[" + Thread.currentThread().getId() + "] worker: " + id + " finished, result: " + done);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
threadB.start();
threadA.start();
try {
threadB.join();
threadA.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
T[12] worker: B ready
T[11] worker: A ready
Exception in thread "Thread-1" T[12] take waiting
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at org.ruoxue.java_147.multithreading.DifferenceWaitNotifyTest$IllegalMonitorWorker.take(DifferenceWaitNotifyTest.java:166)
at org.ruoxue.java_147.multithreading.DifferenceWaitNotifyTest.lambda$5(DifferenceWaitNotifyTest.java:191)
at java.lang.Thread.run(Thread.java:748)
T[11] put finished
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at org.ruoxue.java_147.multithreading.DifferenceWaitNotifyTest$IllegalMonitorWorker.put(DifferenceWaitNotifyTest.java:157)
at org.ruoxue.java_147.multithreading.DifferenceWaitNotifyTest.lambda$4(DifferenceWaitNotifyTest.java:180)
at java.lang.Thread.run(Thread.java:748)
DifferenceWaitNotifyTest.java
Difference Between notify and wait 新增單元測試,驗證是否符合預期。
package org.ruoxue.java_147.synchronization.thread;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
public class DifferenceWaitNotifyTest {
protected class Worker {
private volatile boolean done = false;
public Worker() {
}
public synchronized void put() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] put finished");
done = true;
notify();
}
public synchronized boolean take() throws InterruptedException {
while (!done) {
System.out.println("T[" + Thread.currentThread().getId() + "] take waiting");
wait();
}
return done;
}
}
@Test
public void worker() {
Worker worker = new Worker();
Thread threadA = new Thread(() -> {
String id = "A";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
worker.put();
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
String id = "B";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
boolean done = worker.take();
System.out.println(
"T[" + Thread.currentThread().getId() + "] worker: " + id + " finished, result: " + done);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
threadB.start();
threadA.start();
try {
threadA.join();
threadB.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
protected class InterruptWorker {
private volatile boolean done = false;
public InterruptWorker() {
}
public synchronized void put() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] put finished");
done = true;
// notify();
}
public synchronized boolean take() throws InterruptedException {
while (!done) {
System.out.println("T[" + Thread.currentThread().getId() + "] take waiting");
wait();
}
return done;
}
}
@Test
public void interruptWorker() {
InterruptWorker worker = new InterruptWorker();
Thread threadA = new Thread(() -> {
String id = "A";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
worker.put();
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
String id = "B";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
boolean done = worker.take();
System.out.println(
"T[" + Thread.currentThread().getId() + "] worker: " + id + " finished, result: " + done);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
threadB.start();
threadA.start();
try {
threadA.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
long startTime = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - startTime > 1000) {
threadB.interrupt();
break;
}
}
}
protected class IllegalMonitorWorker {
private volatile boolean done = false;
private final Object lock = new Object();
public IllegalMonitorWorker() {
}
public void put() throws InterruptedException {
synchronized (lock) {
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] put finished");
done = true;
notify();
}
}
public boolean take() throws InterruptedException {
synchronized (lock) {
while (!done) {
System.out.println("T[" + Thread.currentThread().getId() + "] take waiting");
wait();
}
}
return done;
}
}
@Test
public void illegalMonitorWorker() {
IllegalMonitorWorker worker = new IllegalMonitorWorker();
Thread threadA = new Thread(() -> {
String id = "A";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
worker.put();
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
String id = "B";
try {
System.out.println("T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
boolean done = worker.take();
System.out.println(
"T[" + Thread.currentThread().getId() + "] worker: " + id + " finished, result: " + done);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
threadB.start();
threadA.start();
try {
threadB.join();
threadA.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
心得分享
Difference Between wait and notify Methods in Java 操作這兩個方法 wait 和 notify 時,都有可能會拋出 IllegalMonitorStateException ,例如:當 1 個執行緒持有物件 A 的鎖時,並試圖調用物件 B 的 wait 或 notify 時, 就會發生此種情況, Difference Between notify and wait 提供這兩個方法的區別與使用。