「哲學家就餐問題」是經典的並發同步案例:5 位哲學家圍圓桌而坐,每人左右各有一根筷子,需同時拿到兩根筷子才能進餐。若所有哲學家同時拿起左手筷子,將陷入循環等待——死鎖。
核心類設計:
Philosopher 繼承 Thread,循環「思考 -> 獲取筷子 -> 進餐 -> 歸還筷子」。Fork 管理 5 根筷子狀態 boolean[] used,提供 synchronized takeFork() 與 putFork(),利用 wait()/notifyAll() 協調。package cn.edu.sdust.philosopher;
class Philosopher extends Thread {
private final Fork fork;
public Philosopher(String name, Fork fork) {
super(name);
this.fork = fork;
}
@Override
public void run() {
while (true) {
thinking();
fork.takeFork(); // 獲取左右兩根筷子
eating();
fork.putFork(); // 同時歸還
}
}
private void eating() {
System.out.println("I am Eating: " + getName());
sleepQuietly(1000);
}
private void thinking() {
System.out.println("I am Thinking: " + getName());
sleepQuietly(1000);
}
private void sleepQuietly(long ms) {
try { Thread.sleep(ms); } catch (InterruptedException ignored) {}
}
}
class Fork {
private final boolean[] used = new boolean[5]; // false = 空閒
// 同時獲取左右筷子,否則等待
public synchronized void takeFork() {
int i = Integer.parseInt(Thread.currentThread().getName()); // 0~4
while (used[i] || used[(i + 1) % 5]) {
try { wait(); } catch (InterruptedException ignored) {}
}
used[i] = true;
used[(i + 1) % 5] = true;
}
// 同時歸還並喚醒等待者
public synchronized void putFork() {
int i = Integer.parseInt(Thread.currentThread().getName());
used[i] = false;
used[(i + 1) % 5] = false;
notifyAll();
}
}
public class ThreadTest {
public static void main(String[] args) {
Fork fork = new Fork();
for (int i = 0; i < 5; i++) {
new Philosopher(String.valueOf(i), fork).start();
}
}
}
I am Thinking:0
I am Thinking:2
I am Thinking:3
I am Thinking:1
I am Thinking:4
I am Eating:0
I am Eating:2
I am Thinking:0
I am Eating:4
I am Thinking:2
I am Eating:1
I am Thinking:4
I am Eating:3
I am Thinking:1
I am Eating:0
I am Thinking:3
I am Eating:2
I am Thinking:0
I am Eating:4
I am Thinking:2
...
觀察可見:
synchronized 保證了檢查與設置的原子性;wait()/notifyAll() 實現了高效的線程協作。Fork 實例作為唯一鎖,保護整個 used[] 狀態,避免細粒度鎖導致的活鎖。while (條件) wait() 防止虛假喚醒。Integer.parseInt 映射索引。ReentrantLock + Condition 替代 synchronized,支援公平鎖、可中斷等待。Semaphore(4) 限制並發進餐數,簡化邏輯 (方案一)。哲學家就餐問題生動展示了死鎖產生條件與破解策略。通過「原子獲取多資源」配合 wait/notify 協作機制,Java 多線程程式可優雅解決資源競爭難題。掌握此模式,對理解數據庫連接池、線程池、分布式鎖等高級並發組件極有裨益。