線程是 Java 併發編程基石。本文梳理兩種創建方式,並通過「兩線程交替打印時間」案例講解 wait/notify 同步機制。
public class ThreadOne extends Thread {
@Override public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(this.getName() + ":" + i);
}
}
public static void main(String[] args) {
new ThreadOne().start(); // 必須調用 start(),非 run()
}
}
public class MyThread implements Runnable {
@Override public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "放入一個蘋果:" + i);
try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); }
}
}
public static void main(String[] args) {
MyThread task = new MyThread();
new Thread(task).start(); // 同一任務可被多線程共享
}
}
Thread-0 Sun Feb 28 11:21:52 CST 2016
Thread-1 Sun Feb 28 11:21:53 CST 2016
Thread-0 Sun Feb 28 11:21:54 CST 2016
Thread-1 Sun Feb 28 11:21:55 CST 2016
... 共 10 行
Object lock = new Object()int state = 1(1=線程A執行, 2=線程B執行)countA、countB 控制各 5 次wait() → 執行業務 → 修改 state → notifyAll() → 釋放鎖public class ThreadDate {
private static int state = 1;
private static int countA = 0, countB = 0;
private static final Object LOCK = new Object();
public static void main(String[] args) {
// 線程 A
new Thread(() -> {
while (countA < 5) {
synchronized (LOCK) {
while (state != 1) { try { LOCK.wait(); } catch (InterruptedException e) {} }
System.out.println(Thread.currentThread().getName() + " " + new Date());
try { Thread.sleep(1000); } catch (InterruptedException e) {}
state = 2;
LOCK.notifyAll();
}
countA++;
}
}).start();
// 線程 B
new Thread(() -> {
while (countB < 5) {
synchronized (LOCK) {
while (state != 2) { try { LOCK.wait(); } catch (InterruptedException e) {} }
System.out.println(Thread.currentThread().getName() + " " + new Date());
try { Thread.sleep(1000); } catch (InterruptedException e) {}
state = 1;
LOCK.notifyAll();
}
countB++;
}
}).start();
}
}
wait()/notify() 必須在 synchronized 區塊內調用,否則拋 IllegalMonitorStateExceptionwhile 而非 if 判斷條件,防止虛假喚醒notifyAll() 喚醒所有等待線程,配合狀態變量讓目標線程繼續BlockingQueue、CountDownLatch、CyclicBarrier、Phaser 更簡潔安全