javaconcurrencydining-philosopher

Dining philosophers in java


Today, I decide to try to solve the dining philosophers problem. So I write the code below. But I think that it is not correct, so I will be pleased if someone tells me what is wrong with it. I use forks for locks ( I only read them, because of that I don't put access to them in synchronized blocks), I have class that extends thread and it keeps its two locks.

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

}

I think that something is wrong, because the fifth philosopher never eating, mostly the fourth and third philosophers eat. Thanks in advance.


Solution

  • There is a name for your problem: It is called thread "starvation". It's what happens when several threads are competing for the same resource, and one (or more) of them is continually denied access.

    Figuring out how to avoid deadlocks is one aspect of the Dining Philosophers puzzle, but figuring out how to let each philosopher get a fair amount of eating time can be another aspect.

    JP Moresmau's answer suggested that you force each philosopher to take a break (often called "thinking" in the classic puzzle) so that other philosophers get a turn to eat. That works, but if you think of your philosophers as worker threads in some application, then "eating" corresponds the worker thread doing something useful work, and "resting" or "thinking" corresponds to the thread sitting idle, which might be something you'd wish to avoid.

    It takes more than just locks to make sure that all of the philosophers get a fair share of eating time if all of them are always hungry.

    Here's a hint: Higher-level synchronization objects that make any kind of "fairness" guarantee often use queues in the implementation.