/**
* Chandy/Misra solution implementation.
*
* 1. For every pair of philosophers give chopstick to the one with the lower ID.
*
* 2. Each chopstick can either be dirty or clean. Initially, all chopsticks are
* dirty.
*
* 3. When a philosopher wants to eat, he must obtain the chopsticks he does not
* have from his contending neighbors. For all such chopsticks he does not have, he
* sends a request message and waits. See Philosopher.obtainChopsticksIfNecessary()
* and Philosopher.waitForChopstick()
*
* 4. Only dirty chopstick can be given up. When the chopstick sent over, it
* gets cleaned. See Philosopher.giveUpChopstickIfNecessary()
*
* 5. After a philosopher is done eating, all his chopsticks become dirty. See
* Philosopher.eat()
*
* 6. Philosopher can eat only when both chopsticks present. Chopsticks can not
* be sent away when philosopher is eating. Chopsticks change state ONLY inside the
* owner's thread so philosopher does not need to lock them when he's eating.
*
* 7. What he needs however is to check his neighbors periodically in order to
* send them chopsticks when they're ready to eat. See processChopsticks() and
* giveUpChopstickIfNecessary()
*
* The clean/dirty labels give preference to the most "starved" processes. It
* solves the starvation problem.
*
* Initializing the system so that philosophers with lower IDs have dirty
* chopsticks AND continuous tracking neighbor's state gives a guarantee that
* deadlock cannot occur and system has overall progress.
*
*/
class Program {
public static void main
(String[] args
) {
final int philosophersCount = 6;
final int eatCount = 5;
Philosopher[] philosophers = new Philosopher[philosophersCount];
Chopstick[] chopsticks = new Chopstick[philosophersCount];
for (int i = 0; i < philosophersCount; i++)
chopsticks[i] = new Chopstick(i + 1);
for (int i = 0; i < philosophersCount; i++)
philosophers[i] = new Philosopher(i + 1, chopsticks[i],
chopsticks[(i + 1) % philosophersCount], eatCount);
// set neighbors and assign owners: give the chopsticks to the philosopher with the lower ID
philosophers[0].leftNeighbor = philosophers[philosophersCount - 1];
philosophers[0].rightNeighbor = philosophers[1];
chopsticks[0].owner = philosophers[0];
for (int i = 1; i < philosophersCount; i++) {
philosophers[i].leftNeighbor = philosophers[(i - 1) % philosophersCount];
philosophers[i].rightNeighbor = philosophers[(i + 1) % philosophersCount];
chopsticks[i].owner = philosophers[i - 1];
}
System.
out.
println("Dinner is starting!\n"); for (int i = 0; i < philosophersCount; i++)
philosophers[i].start();
// wait all threads to complete
for (int i = 0; i < philosophersCount; i++)
philosophers[i].join();
System.
out.
println("Dinner is over!"); }
}
final int id;
final Chopstick leftChopstick, rightChopstick;
final int eatCount;
Philosopher leftNeighbor, rightNeighbor;
int eatNum = 0; // total # times Philosopher has eaten
int eatInRaw = 0; // how many times Philosopher has eaten in a row
boolean goingToEatRequest = false; // signal "I want to eat"
public Philosopher(int id, Chopstick leftChopstick,
Chopstick rightChopstick, int eatCount) {
super();
this.id = id;
this.leftChopstick = leftChopstick;
this.rightChopstick = rightChopstick;
this.eatCount = eatCount;
}
/**
* Main loop
*/
public void run() {
while (eatNum < eatCount) {
think();
processChopsticks(true);
obtainChopsticksIfNecessary();
eat();
processChopsticks(eatNum != eatCount); // give up chopsticks unconditionally after the last iteration
}
}
void obtainChopsticksIfNecessary() {
// indicate that we need chopsticks so the neighbors send them
goingToEatRequest = true;
waitForChopstick(leftChopstick);
waitForChopstick(rightChopstick);
goingToEatRequest = false;
}
/**
* Monitor "Guarded Wait" implementation
*/
void waitForChopstick(Chopstick chopstick) {
if (chopstick.owner != this) {
synchronized (chopstick) {
// System.out.format("Philosopher %d WAITING for chopstick %d.\n",
// id, chopstick.id);
try {
while (chopstick.owner != this) {
// while waiting, Philosopher must be able to release
// dirty chopstick he owns to avoid deadlock
processChopsticks(true);
chopstick.wait();
}
System.
out.
println("InterruptedException caught"); }
}
}
System.
out.
format("Philosopher %d picks up %s chopstick.\n", id, chopstick
== leftChopstick
? "left" : "right"); }
/**
* Philosopher is checking his neighbor's state periodically and releases
* chopsticks synchronously, in his thread. If isRequestRequired set to
* true philosopher can send chopstick he owns only if the neighbor requested
* it. If isRequestRequired is false he can release chopstick regardless
* of neighbor's request (useful when he stops checking neighbor's state).
*/
void processChopsticks(boolean isRequestRequired) {
giveUpChopstickIfNecessary(leftChopstick, leftNeighbor, isRequestRequired);
giveUpChopstickIfNecessary(rightChopstick, rightNeighbor, isRequestRequired);
}
/**
* Philosopher gives up only dirty chopstick he owns,
* on request OR unconditionally (e.g. if he's finished dinner
* and not going to track his neighbor's requests anymore)
*/
void giveUpChopstickIfNecessary(Chopstick chopstick, Philosopher receiver, boolean isRequestRequired) {
synchronized (chopstick) {
if ( (receiver.goingToEatRequest || !isRequestRequired) && // give up chopstick only on request OR unconditionally
!chopstick.isClean && // only dirty chopstick
chopstick.owner == this) { // only chopstick he owns
chopstick.isClean = true; // wash chopstick before sending out
chopstick.owner = receiver;
eatInRaw = 0; // reset # eats-in-raw because chopstick sent out
// System.out.format("Chopstick %d sent to Philosopher %d by Philosopher %d\n", chopstick.id,
// chopstick.owner.id, id);
chopstick.notify(); // notify waiting threads
}
}
}
void eat() {
eatInRaw++;
eatNum++;
rightChopstick.isClean = leftChopstick.isClean = false;
String eatersList
= eaters.
addEater(this); System.
out.
format("Philosopher %d starts eating. Eats in raw: %d. Eating at present: %s.\n",
id, eatInRaw, eatersList);
// System.out.format("Philosopher %d eats.\n", id);
StaticMethods.sleep(StaticMethods.random(100, 200));
eaters.removeEater(this);
// System.out.format("Philosopher %d finished eating (%d times).\n", id, eatNum);
System.
out.
format("Philosopher %d puts down right chopstick.\n", id
); System.
out.
format("Philosopher %d puts down left chopstick.\n", id
); }
void think() {
// System.out.format("Philosopher %d thinks.\n", id);
StaticMethods.sleep(StaticMethods.random(100, 200));
}
public void start() {
thread.start();
}
public void join() {
try {
thread.join();
System.
out.
println("InterruptedException caught"); }
}
}
class Chopstick {
final int id;
volatile Philosopher owner;
volatile boolean isClean = false;
public Chopstick(int id) {
super();
this.id = id;
}
}
/**
* Auxiliary static methods.
*
*/
class StaticMethods {
/**
* Generates random integer [Min, Max]
*/
public static int random(int Min, int Max) {
return Min
+ (int) (Math.
random() * ((Max
- Min
) + 1)); }
/**
* Sleeps delay ms
*/
public static void sleep(int delay)
{
try {
System.
out.
println("InterruptedException caught"); }
}
}
/**
* Auxiliary class. Tracks persons eating at present
*
*/
class eaters {
static java.util.List<Philosopher> eaters = new java.util.ArrayList<Philosopher>();
public synchronized static String addEater
(Philosopher philosopher
) { eaters.add(philosopher);
return eatersToString();
}
public synchronized static String removeEater
(Philosopher philosopher
) { eaters.remove(philosopher);
return eatersToString();
}
private static String eatersToString
() { for (Philosopher philosopher : eaters)
s += philosopher.id + " ";
return s.trim();
}
}