这是我对哲学家晚餐并发问题的实施:
5个哲学家,每个人都延伸线程:
问题是每次程序在死锁内完成。我尝试了不同的解决方案,但没有人解决问题
也许有人可以给我一个帮助。
这是我的计划:
import java.util.concurrent.ThreadLocalRandom;
class Fork {
public static final char FORK = '|';
public static final char NO_FORK = ' ';
int id;
public Fork(final int id) {
this.id = id;
}
}
class Philosopher extends Thread {
public static final char PHIL_THINKING = '-';
public static final char PHIL_LEFT_FORK = '=';
public static final char PHIL_EATING = 'o';
private final int id;
public Philosopher(final int id) {
this.id = id;
}
@Override
public void run() {
final int tableOffset = 4 * id;
final Object leftLock = S5Philosophers.listOfLocks[id];
final Object rightLock = S5Philosophers.listOfLocks[(id + 1)
% S5Philosophers.NUM_PHILOSOPHERS];
final int table__farL = tableOffset + 0;
final int table__left = tableOffset + 1;
final int table_philo = tableOffset + 2;
final int table_right = tableOffset + 3;
final int table__farR = (tableOffset + 4)
% (4 * S5Philosophers.NUM_PHILOSOPHERS);
while (!isInterrupted()) {
try {
Thread.sleep(S5Philosophers.UNIT_OF_TIME
* (ThreadLocalRandom.current().nextLong(6)));
} catch (final InterruptedException e) {
break;
}
// Try to get the chopstick on the left
synchronized (leftLock) {
synchronized (S5Philosophers.class) {
S5Philosophers.dinerTable[table__farL] = Fork.NO_FORK;
S5Philosophers.dinerTable[table__left] = Fork.FORK;
S5Philosophers.dinerTable[table_philo] = PHIL_LEFT_FORK;
}
try {
sleep(S5Philosophers.UNIT_OF_TIME * 1);
} catch (final InterruptedException e) {
break;
}
// Try to get the chopstick on the right
synchronized (rightLock) {
synchronized (S5Philosophers.class) {
S5Philosophers.dinerTable[table_philo] = PHIL_EATING;
S5Philosophers.dinerTable[table_right] = Fork.FORK;
S5Philosophers.dinerTable[table__farR] = Fork.NO_FORK;
//notify();
}
try {
sleep(S5Philosophers.UNIT_OF_TIME * 1);
} catch (final InterruptedException e) {
break;
}
// Release fork
synchronized (S5Philosophers.class) {
S5Philosophers.dinerTable[table__farL] = Fork.FORK;
S5Philosophers.dinerTable[table__left] = Fork.NO_FORK;
S5Philosophers.dinerTable[table_philo] = PHIL_THINKING;
S5Philosophers.dinerTable[table_right] = Fork.NO_FORK;
S5Philosophers.dinerTable[table__farR] = Fork.FORK;
//notify();
}
}
}
}
}
}
public class S5Philosophers {
public static final int NUM_PHILOSOPHERS = 5;
public static final int UNIT_OF_TIME = 50;
public static final Fork[] listOfLocks = new Fork[NUM_PHILOSOPHERS];
public static char[] dinerTable = null;
static {
for (int i = 0; i < NUM_PHILOSOPHERS; i++)
listOfLocks[i] = new Fork(i);
}
public static void main(final String[] a) {
final char[] lockedDiner = new char[4 * NUM_PHILOSOPHERS];
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
lockedDiner[4 * i + 0] = Fork.NO_FORK;
lockedDiner[4 * i + 1] = Fork.FORK;
lockedDiner[4 * i + 2] = Philosopher.PHIL_LEFT_FORK;
lockedDiner[4 * i + 3] = Fork.NO_FORK;
}
final String lockedString = new String(lockedDiner);
// safe publication of the initial representation
synchronized (S5Philosophers.class) {
dinerTable = new char[4 * NUM_PHILOSOPHERS];
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
dinerTable[4 * i + 0] = Fork.FORK;
dinerTable[4 * i + 1] = Fork.NO_FORK;
dinerTable[4 * i + 2] = Philosopher.PHIL_THINKING;
dinerTable[4 * i + 3] = Fork.NO_FORK;
}
}
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
final Thread t = new Philosopher(i);
// uses this solution to allow terminating the application even if
// there is a deadlock
t.setDaemon(true);
t.start();
}
System.out.println("The diner table:");
long step = 0;
while (true) {
step++;
String curTableString = null;
synchronized (S5Philosophers.class) {
curTableString = new String(dinerTable);
}
System.out.println(curTableString + " " + step);
if (lockedString.equals(curTableString))
break;
try {
Thread.sleep(UNIT_OF_TIME);
} catch (final InterruptedException e) {
System.out.println("Interrupted.");
}
}
System.out.println("The diner is locked.");
}
}
答案 0 :(得分:2)
解决方案相对简单:
- 哲学家试图在左边岔叉 成功 - &gt;继续第2步 失败 - &gt;等(暂时)
- 哲学家试图在右边分叉 成功 - &gt;继续第3步 失败 - &gt;释放左叉并等待(暂时)
- 吃掉并释放两个叉子。然后等待(一会儿)
醇>
这里强调的一点是,无论什么时候哲学家都没有得到两个分叉,他需要丢弃他所持有的任何分叉并等待或最终会发生死锁。
也许更重要的是,什么样的白痴使用两把叉子吃?
- 编辑 -
以下是Fork的快速示例
class Fork {
public static final char FORK = '|';
public static final char NO_FORK = ' ';
private int id;
private Lock lock = new ReentrantLock();
public Fork(final int id) {
this.id = id;
}
public boolean isHeld() {
return lock.isLocked();
}
// returns true if successfully grabbed!
public synchronized boolean tryToGrab() {
return lock.tryLock();
}
public void letGo() {
lock.unlock();
}
}
您使用Semaphore对象的想法也同样有用。祝你好运!
答案 1 :(得分:0)
你的策略是首先锁定左分叉(或筷子?似乎你不能决定......)。但每个哲学家都有不同的“左叉”概念。应该很容易想象,有可能进入每个哲学家在第一步锁定他/她的“左叉”的情况,因此没有人可以继续,因为“右叉”被看到的人锁定它是他/她的“左叉”。
但您应该从调试输出中识别出这种情况......
作为提示如何解决这样的问题:可以在单个int
值内表示整个表状态,将每个fork分配一位。因此,对于多达32位哲学家来说就足够了。现在每个哲学家都会用一个位掩码来初始化,告诉他/她需要哪些叉子。然后可以使用单个atomic update一次分配叉子/筷子。因此,没有必要处理单一的叉子。分配可能会成功,也可能无法保持状态不变。除了总是分配的叉子之外,哲学家唯一要记住的是他现在是否拥有这两个叉子。在他吃完之后,他会将两把叉子放回一次更新中。
这种策略可以解决僵局问题,但不能解决一位哲学家在理论上可能存在的饥饿问题(虽然这种情况不太可能)。对于我的现实生活应用程序而言,我从不创建依赖于锁公平性的多线程代码。
但是如果你想完全排除饥饿的可能性,你可以将上述算法扩展到AbstractQueuedSynchronizer
的子类。此类是使用原子int
表示状态并支持等待以使所需资源(位)变为可用的概念的扩展。它的课程文档描述了如何实现公平等待,所以没有哲学家必须挨饿......
答案 2 :(得分:0)
我没有答案,但我有几点建议:
(1)这是次要的,但不要覆盖Thread:Override Runnable。这是一个好习惯,但我没有时间解释原因。就这么做。
class Philosopher implements Runnable { ... }
...
Thread t = new Thread(new Philosopher());
(2)不要使用synchronized
来解决这类问题:使用java.util.concurrent.locks.Lock可以更灵活地构建代码。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
Lock[] listOfLocks = new Lock[NUM_PHILOSOPHERS];
for (int i=0 ; i<listOfLocks.length ; i++) {
listOfLocks[i] = new ReentrantLock();
}
(3)正如@Holger指出的那样,你选择了一种容易出现死锁的策略;你必须尝试别的东西。不幸的是,您的策略深深嵌入您的代码中。这意味着您尝试的每个新策略都会进行大量的重写。
考虑如何将策略与其余代码隔离开来。如果您定义了“策略”,该怎么办?如果您的每个哲学家实例都使用策略来抓住筷子,那该怎么办呢?
interface Strategy {
//returns after locking both the leftLock and the rightLock
public void grabSticks(Lock leftLock, Lock rightLock);
}
class Philosopher implements Runnable {
private final Strategy strategy;
public Philosopher(Strategy strategy) {
this.strategy = strategy;
}
@Override
public void run() {
...
while (...) {
think();
strategy.grabSticks(leftLock, rightLock);
eat();
leftLock.unlock();
rightLock.unlock();
}
}
}
现在,每次你想尝试不同的东西时,你只需要改变grabSticks()方法......
......或方法!每个哲学家都没有必要使用相同的策略。
我的策略界面是否足够好?无论如何,这是一个开始,但也许你可以改进它。是否可以扩展为哲学家提供一些更复杂的合作方式,而不仅仅是悄悄地抓住筷子?
祝你好运,玩得开心!