java 中的哲学家用餐
Dining philosophers in java
今天,我决定尝试解决哲学家就餐问题。所以我写了下面的代码。但我认为这是不正确的,所以如果有人告诉我它有什么问题,我会很高兴。我使用 forks 作为锁(我只读它们,因为我没有在同步块中访问它们),我有 class 扩展线程并保留它的两个锁。
import java.util.Random;
public class EatingPhilosophersProblem {
private final static Random RANDOM = new Random();
/**
*
* @author Damyan Class represents eating of every philosopher. It
* represents infinity cycle of eating.
*/
private static class PhilosopherEating extends Thread {
int forkOne;
int forkTwo;
public PhilosopherEating(String name, int forkOne, int forkTwo) {
super(name);
this.forkOne = forkOne;
this.forkTwo = forkTwo;
}
@Override
public void run() {
super.run();
while (true) {
requireLock(this, forkOne, forkTwo);
}
}
}
private static Boolean[] forks = new Boolean[] { new Boolean(true), new Boolean(true), new Boolean(true),
new Boolean(true), new Boolean(true) };
// locks should be created by new, otherwise almost 100% sure that they will
// point to the same object (because of java pools)
// this pools are used from java for immutable objects
private static void requireLock(PhilosopherEating philosopherEating, int firstIndex, int secondIndex) {
// we lock always from the the lower index to the higher, otherwise
// every philosopher can take his left fork and deadlock will apear
if (firstIndex > secondIndex) {
int temp = firstIndex;
firstIndex = secondIndex;
secondIndex = temp;
}
if (firstIndex == 4 || secondIndex == 4) {
System.err.println(firstIndex + " and " + secondIndex);
}
synchronized (forks[firstIndex]) {
synchronized (forks[secondIndex]) {
printPhilosopherhAction(philosopherEating, "start eating");
try {
Thread.sleep(RANDOM.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
printPhilosopherhAction(philosopherEating, "stop eating");
}
}
};
private static void printPhilosopherhAction(PhilosopherEating philosopherEating, String action) {
System.out.println("Philosopher " + philosopherEating.getName() + " " + action);
}
public static void main(String[] args) {
PhilosopherEating first = new PhilosopherEating("1 - first", 0, 1);
PhilosopherEating second = new PhilosopherEating("2 - second", 1, 2);
PhilosopherEating third = new PhilosopherEating("3 - third", 2, 3);
PhilosopherEating fourth = new PhilosopherEating("4 - fourth", 3, 4);
PhilosopherEating fifth = new PhilosopherEating("5 - fifth", 4, 0);
first.start();
second.start();
third.start();
fourth.start();
fifth.start();
}
我觉得有点不对劲,因为第五位哲学家从不吃饭,主要是第四位和第三位哲学家吃饭。
提前致谢。
你在哲学家吃饭时锁定,时间长度随机,但随后你不断循环,所以当其他线程收到锁已解除的通知时,你的第一个哲学家又开始吃饭了。如果我把你的代码修改成吃完锁外随机等待:
requireLock(this, forkOne, forkTwo);
try {
Thread.sleep(RANDOM.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
我看到所有的哲学家都更擅长轮流吃饭。
您的问题有一个名称:线程 "starvation"。当多个线程竞争同一个资源,并且其中一个(或多个)线程不断被拒绝访问时,就会发生这种情况。
弄清楚如何避免死锁是 Dining Philosophers 难题的一个方面,但弄清楚如何让每个哲学家获得公平的用餐时间可能是另一个方面。
JP Moresmau 的回答建议你强迫每个哲学家休息一下(在经典谜题中通常称为 "thinking"),以便其他哲学家轮流吃饭。这行得通,但是如果您将哲学家视为某些应用程序中的工作线程,那么 "eating" 对应于工作线程执行一些有用的工作,而 "resting" 或 "thinking" 对应于闲置的线程,这可能是您希望避免的事情。
如果所有的哲学家总是饿着肚子,要确保所有的哲学家都能公平地分享吃饭时间,光靠锁是不够的。
这里有一个提示:做出任何类型 "fairness" 保证的更高级别的同步对象通常在实现中使用队列。
今天,我决定尝试解决哲学家就餐问题。所以我写了下面的代码。但我认为这是不正确的,所以如果有人告诉我它有什么问题,我会很高兴。我使用 forks 作为锁(我只读它们,因为我没有在同步块中访问它们),我有 class 扩展线程并保留它的两个锁。
import java.util.Random;
public class EatingPhilosophersProblem {
private final static Random RANDOM = new Random();
/**
*
* @author Damyan Class represents eating of every philosopher. It
* represents infinity cycle of eating.
*/
private static class PhilosopherEating extends Thread {
int forkOne;
int forkTwo;
public PhilosopherEating(String name, int forkOne, int forkTwo) {
super(name);
this.forkOne = forkOne;
this.forkTwo = forkTwo;
}
@Override
public void run() {
super.run();
while (true) {
requireLock(this, forkOne, forkTwo);
}
}
}
private static Boolean[] forks = new Boolean[] { new Boolean(true), new Boolean(true), new Boolean(true),
new Boolean(true), new Boolean(true) };
// locks should be created by new, otherwise almost 100% sure that they will
// point to the same object (because of java pools)
// this pools are used from java for immutable objects
private static void requireLock(PhilosopherEating philosopherEating, int firstIndex, int secondIndex) {
// we lock always from the the lower index to the higher, otherwise
// every philosopher can take his left fork and deadlock will apear
if (firstIndex > secondIndex) {
int temp = firstIndex;
firstIndex = secondIndex;
secondIndex = temp;
}
if (firstIndex == 4 || secondIndex == 4) {
System.err.println(firstIndex + " and " + secondIndex);
}
synchronized (forks[firstIndex]) {
synchronized (forks[secondIndex]) {
printPhilosopherhAction(philosopherEating, "start eating");
try {
Thread.sleep(RANDOM.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
printPhilosopherhAction(philosopherEating, "stop eating");
}
}
};
private static void printPhilosopherhAction(PhilosopherEating philosopherEating, String action) {
System.out.println("Philosopher " + philosopherEating.getName() + " " + action);
}
public static void main(String[] args) {
PhilosopherEating first = new PhilosopherEating("1 - first", 0, 1);
PhilosopherEating second = new PhilosopherEating("2 - second", 1, 2);
PhilosopherEating third = new PhilosopherEating("3 - third", 2, 3);
PhilosopherEating fourth = new PhilosopherEating("4 - fourth", 3, 4);
PhilosopherEating fifth = new PhilosopherEating("5 - fifth", 4, 0);
first.start();
second.start();
third.start();
fourth.start();
fifth.start();
}
我觉得有点不对劲,因为第五位哲学家从不吃饭,主要是第四位和第三位哲学家吃饭。 提前致谢。
你在哲学家吃饭时锁定,时间长度随机,但随后你不断循环,所以当其他线程收到锁已解除的通知时,你的第一个哲学家又开始吃饭了。如果我把你的代码修改成吃完锁外随机等待:
requireLock(this, forkOne, forkTwo);
try {
Thread.sleep(RANDOM.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
我看到所有的哲学家都更擅长轮流吃饭。
您的问题有一个名称:线程 "starvation"。当多个线程竞争同一个资源,并且其中一个(或多个)线程不断被拒绝访问时,就会发生这种情况。
弄清楚如何避免死锁是 Dining Philosophers 难题的一个方面,但弄清楚如何让每个哲学家获得公平的用餐时间可能是另一个方面。
JP Moresmau 的回答建议你强迫每个哲学家休息一下(在经典谜题中通常称为 "thinking"),以便其他哲学家轮流吃饭。这行得通,但是如果您将哲学家视为某些应用程序中的工作线程,那么 "eating" 对应于工作线程执行一些有用的工作,而 "resting" 或 "thinking" 对应于闲置的线程,这可能是您希望避免的事情。
如果所有的哲学家总是饿着肚子,要确保所有的哲学家都能公平地分享吃饭时间,光靠锁是不够的。
这里有一个提示:做出任何类型 "fairness" 保证的更高级别的同步对象通常在实现中使用队列。