在java中用餐的哲学家

时间:2015-07-23 19:54:55

标签: java concurrency dining-philosopher

今天,我决定尝试解决餐饮哲学家的问题。所以我写下面的代码。但我认为这不正确,所以如果有人告诉我它有什么问题,我会很高兴。我使用forks进行锁定(我只读它们,因为我没有在同步块中访问它们),我有一个扩展线程的类,它保留了两个锁。

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();

}

我觉得有些不对劲,因为第五位哲学家从不吃东西,大多数是第四位和第三位哲学家吃的。 提前谢谢。

2 个答案:

答案 0 :(得分:1)

您的问题有一个名称:它被称为线程"饥饿"。当几个线程竞争相同的资源时,会发生什么,并且一个(或多个)线程不断被拒绝访问。

弄清楚如何避免死锁是餐饮哲学家之谜的一个方面,但弄清楚如何让每个哲学家获得相当多的饮食时间可能是另一个方面。

JP Moresmau的回答表明,你强迫每个哲学家休息一下(通常称为#34;思考"在经典之谜中),以便其他哲学家转而吃饭。这是有效的,但如果你认为你的哲学家在某些应用中是工人的话,那么" eat"对应工作者线程做一些有用的工作,并且"休息"或者"思考"对应于闲置的线程,这可能是您希望避免的。

如果所有的哲学家都总是在饥饿的情况下,确保所有哲学家都能获得相当多的吃饭时间,那么这需要的不仅仅是锁定。

这里有一个提示:更高级别的同步对象可以产生任何类型的“公平”和#34;保证经常在实现中使用队列。

答案 1 :(得分:0)

当哲学家正在吃饭时,你会在一段时间内进行锁定,但随后你会不断循环,所以当其他线程被告知已解除锁定时,你的第一位哲学家已经开始再次进食。如果我修改你的代码以便在锁外吃完后随机等待:

requireLock(this, forkOne, forkTwo);
try {
  Thread.sleep(RANDOM.nextInt(100));
} catch (InterruptedException e) {
  e.printStackTrace();
}

我看到所有的哲学家都更好地依次吃着它们。