I need to do a pretty standard dining philosophers setup for my class. (I assume most people on here know what this problem is... if not look here: http://en.wikipedia.org/wiki/Dining_philosophers_problem)
We need to have a variable number of philosophers between 2 and 8, and avoid deadlock and starvation.
In my design, each philosopher thread asks a Monitor class whether it can eat.
The monitor class knows which chopsticks are to the left and right of the requesting philosopher,
so it checks whether the left chopstick is free. If not, it waits three seconds, puts it down, and tries again.
If it is free, it tries to pick up the right chopstick. If it's free, great! The philosopher eats. If it's not free, the philosopher puts down the left chopstick. Then he tries all over again.
Each philosopher is supposed to go through the cycle five times. But my code is freezing really early. There seems to be some kind of deadlock, because the last printed output is "Philosopher #i is hungry and looking for chopsticks." Like this:
Philosopher #1 is thinking deep thoughts.
Philosopher #2 is thinking deep thoughts.
Philosopher #3 is thinking deep thoughts.
Philosopher #2 is hungry and looking for chopsticks.
Philosopher #2 is eating.
Philosopher #3 is hungry and looking for chopsticks.
Philosopher #1 is hungry and looking for chopsticks.
Please help!! My code is below.
public class Philosopher implements Runnable {
public final int EATS = 5;
private int philNum;
private int timesEaten;
private Monitor m;
private int state; // 0=thinking, 1=hungry, 2=eating, 3=stuffed
Philosopher(int num, Monitor mon) {
philNum = num;
m=mon;
state=0;
}
public int getPhilNum() {
return philNum;
}
public void run () {
try {
// Do I need to put this in a while loop?
for (int i=0; i<EATS; i++) {
System.out.println("Philosopher #"+philNum+" is thinking deep thoughts.");
Thread.sleep((long)Math.random()*3000); // The philosopher is thinking
state=1; // The philosopher gets hungry
// The philosopher tells the view to change his state
System.out.println("Philosopher #"+philNum+" is hungry and looking for chopsticks.");
m.requestEat(philNum); // The philosopher asks the monitor if he can eat
System.out.println("Philosopher #"+philNum+" is eating.");
state=2;
Thread.sleep((long)Math.random()*2000); // The philosopher is eating
m.stopEating(philNum);
state=3;
System.out.println("Philosopher #"+philNum+" is stuffed and putting down his chopsticks.");
Thread.sleep((long)Math.random()*2000); // The philosopher is stuffed
state=0;
timesEaten++;
if (timesEaten==5) System.out.println("PHILOSOPHER #"+philNum+" HAS EATEN "+timesEaten+" TIMES.");
else System.out.println("Philosopher #"+philNum+" has eaten "+timesEaten+" times.");
}
}
catch (InterruptedException e) { }
}
}
import java.util.concurrent.locks.*;
import java.util.concurrent.*;
public class Chopstick {
private boolean inUse=false;
private int stickNum;
private ReentrantLock lock = new ReentrantLock();
private Condition chopstickAvailableCondition = lock.newCondition();
Chopstick(int num) {
stickNum=num;
}
synchronized boolean isInUse() {
return inUse;
}
synchronized void putDown() {
inUse=false;
chopstickAvailableCondition.signal();
lock.unlock();
}
synchronized boolean pickUp() {
try {
lock.lock();
if (inUse) {
if (chopstickAvailableCondition.await(3, TimeUnit.SECONDS)) {
inUse=true;
return true;
}
else {
lock.unlock();
return false;
}
}
else {
inUse=true;
return true;
}
}
catch (InterruptedException e) { return false; }
}
}
import java.util.*;
public class Monitor {
LinkedList<Philosopher> philQueue;
private int numPhils;
Chopstick[] chopsticks;
Monitor(int numOfPhils) {
numPhils=numOfPhils;
chopsticks = new Chopstick[numPhils];
for (int i=0; i<numPhils; i++) {
chopsticks[i]=new Chopstick(i+1);
}
philQueue = new LinkedList<Philosopher>();
}
synchronized boolean requestEat(int pNum) {
int currentPhil=pNum;
int left = pNum-1; // The INDEX position of the chopstick with the same number, so subtract 1
int right;
if (pNum==numPhils) right = 0;
else right = pNum; // The INDEX position of the chopstick with (the philosopher's number + 1)
boolean success=false;
if (chopsticks[left].pickUp()) {
if (chopsticks[right].pickUp()) {
success=true;
}
else chopsticks[left].putDown();
}
return success;
}
synchronized void stopEating(int pNum) {
int left = pNum-1;
int right;
if (pNum==numPhils) right = 0;
else right = pNum;
chopsticks[left].putDown();
chopsticks[right].putDown();
}
synchronized boolean checkChopsticks(int pNum) {
int left = pNum-1;
int right;
if (pNum==numPhils) right = 0;
else right = pNum;
if (!chopsticks[left].isInUse() && !chopsticks[right].isInUse()) return true;
else return false;
}
}
public class DinnerMain {
public static void main (String[] args) {
try {
int numPhils = Integer.parseInt(args[0]);
Monitor m;
if (numPhils >= 2 && numPhils <= 8) {
m = new Monitor(numPhils);
Philosopher[] philosophers = new Philosopher[numPhils];
for (int i=0; i<numPhils; i++) {
philosophers[i]=new Philosopher(i+1, m);
}
for (int i=0; i<numPhils; i++) {
new Thread(philosophers[i]).start();
}
}
else {
System.out.println("Your argument was not an integer between 2 and 8." +
"Please try again with a valid command line argument.");
}
}
catch (NumberFormatException e) {
System.out.println("Your argument was not an integer between 2 and 8." +
"Please try again with a valid command line argument.");
}
}
}