Java实现Dining Philosophers时Thread.join()异常,程序无限运行求助
排查Java Dining Philosophers程序无限运行、join()阻塞的问题
看起来你的问题核心在于线程无法正常终止,导致philosopher[i].t.join();一直卡住——这在Dining Philosophers的实现里是个很常见的坑,我帮你拆解几个最可能的原因,以及对应的解决思路:
1. 线程缺少终止条件(最常见)
如果你的哲学家线程是无限循环执行思考-进食流程,那线程永远不会主动结束,join()自然会一直阻塞等待。比如你的run()方法可能是这样的:
public void run() { while (true) { // 无限循环,没有退出逻辑 think(); pickUpChopsticks(); eat(); putDownChopsticks(); } }
解决办法:给线程添加退出条件
你可以给每个哲学家设置一个“最大进食次数”,或者用一个volatile boolean变量来控制全局停止:
class Philosopher extends Thread { private int id; private int eatCount = 0; private final int MAX_EATS = 5; // 比如每个哲学家吃5次就退出 private Chopstick left, right; public Philosopher(int id, Chopstick left, Chopstick right) { this.id = id; this.left = left; this.right = right; } @Override public void run() { while (eatCount < MAX_EATS) { // 达到次数就终止循环 think(); pickUpChopsticks(); eat(); putDownChopsticks(); eatCount++; } System.out.println("哲学家" + id + "完成进食,线程退出"); } private void think() { try { Thread.sleep((long)(Math.random()*1000)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("哲学家" + id + "正在思考"); } // 其他方法... }
2. 死锁依然存在(你以为没有,但实际有)
即使你觉得已经避免了死锁,也可能因为拿筷子的顺序问题导致循环等待。比如经典的“每个哲学家先拿左筷子,再拿右筷子”,当5个哲学家同时拿起左筷子时,所有人都会卡在等待右筷子的状态,线程全部阻塞,join()永远等不到。
解决办法:打破循环等待条件
最简单的方式是让最后一个哲学家反过来拿筷子(先拿右,再拿左),打破循环依赖:
private void pickUpChopsticks() { if (id == 4) { // 假设哲学家ID从0到4,最后一个哲学家换顺序 right.pickUp(); left.pickUp(); } else { left.pickUp(); right.pickUp(); } System.out.println("哲学家" + id + "拿起了两根筷子"); }
另外也可以用“最多允许4个哲学家同时拿筷子”的方式,从根本上避免死锁。
3. 筷子的锁释放逻辑错误
如果你的putDownChopsticks()方法没有正确释放锁,比如同步块的范围不对,或者漏释放了某根筷子,会导致其他哲学家永远拿不到对应的筷子,线程卡住。
检查要点:
- 确保
pickUp和putDown都是用synchronized修饰(或者用显式锁的lock()/unlock()配对) - 确保每次
pickUp之后,一定会执行putDown(比如用try-finally包裹,避免异常导致锁未释放):
class Chopstick { private final int id; private final ReentrantLock lock = new ReentrantLock(); public Chopstick(int id) { this.id = id; } public void pickUp() { lock.lock(); System.out.println("筷子" + id + "被拿起"); } public void putDown() { lock.unlock(); System.out.println("筷子" + id + "被放下"); } }
调试小技巧
如果还是找不到问题,可以用JDK自带的jstack工具查看线程状态:
- 找到程序的PID(用
jps命令) - 执行
jstack <PID>,查看每个哲学家线程的状态:- 如果线程状态是
BLOCKED,说明死锁了 - 如果是
RUNNABLE,说明线程还在无限循环执行思考/进食
- 如果线程状态是
内容的提问来源于stack exchange,提问作者T. John.
相关产品推荐
相关产品推荐

