Sunday, January 31, 2016

Dining Philosophers Problem

I recently came across the Dining Philosphers problem and I thought it would be a fun way to teach myself a little about threads in Java. Basically, the problem is that there are an N number of philosophers sitting around a circular table. Between each philosopher is a single chopstick. A philosopher can only eat the food in front of him if he can pick up both the chopstick to his left and to his right. If he is not eating, then he is thinking. The tough part is to get all threads to work together without a philosopher starving or eating forever. Below is my solution:

/*
 * Dining Philosophers Problem
 * Author: Justin Miller
 * Date: 1/31/16
 */

import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

/*
 * Main class takes in number of philosophers and creates them and chopsticks and starts threads
 */
public class PhilosopherProblem{
private static Chopstick[] chopsticks;
private  static int num_philosophers;

public static void main(String[] args){
System.out.println("Enter number of philosophers at the table");
Scanner kb = new Scanner(System.in);
num_philosophers = kb.nextInt();
System.out.println("Number of philosophers: " + num_philosophers);

//makes number of chopsticks required for each philosopher
makeChopsticks();

//creates and starts threads
int count;
for(int i=0; i<num_philosophers; i++){
count = i-1;
if(count<0)
count = num_philosophers - 1;
DiningPhilosopher temp = new DiningPhilosopher(i+1,chopsticks[count],chopsticks[i]);
temp.start();
}
}

//method to make chopsticks based on how many philosophers created
private static void makeChopsticks(){
chopsticks = new Chopstick[num_philosophers];

for(int i=0; i<chopsticks.length; i++){
chopsticks[i] = new Chopstick();
}
}
}

/*
 * gets and sets whether chopstick is in use
 */
class Chopstick {
    boolean taken = false;

    public synchronized void take() throws InterruptedException {
        while (taken) {
            wait();
        }
        taken = true;
    }

    public synchronized void drop() {
        taken = false;
        notifyAll();
    }
}

/*
 * Class to create DiningPhilosopher objects
 */
class DiningPhilosopher extends Thread{
private Random rand = new Random();
private int name;
private Chopstick leftChopstick;
private Chopstick rightChopstick;
private State state;
private boolean thinking = false;

public DiningPhilosopher(int name, Chopstick leftChopstick, Chopstick rightChopstick){
System.out.println("Philosopher " + name + " has started");
this.leftChopstick = leftChopstick;
this.rightChopstick = rightChopstick;
this.name = name;
}

//run method thinks, then tries to eat
public void run() {  
   try {
    while (!Thread.interrupted()) {
think();
eat();
    }
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

//If philosopher has just eaten, method prints "thinking"
//else it waits a random amount of time before returning to run
public void think() throws InterruptedException {
if(thinking == false) {
System.out.println("Philosopher " + name + " " + "thinking");
}
thinking = true;
Thread.sleep(rand.nextInt(800));
}

//checks if ready to eat, gets chopsticks. Eats a random amount of time before setting chopsticks
public void eat() throws InterruptedException{
if(leftChopstick.taken == false){
if(rightChopstick.taken == false) {
System.out.println("Philosopher " + name + " " + "eating");
getChopsticks();
Thread.sleep(rand.nextInt(800));
setChopsticks();
thinking = false;
}
}
}

//makes chopsticks unusable to other philosophers
public void getChopsticks() throws InterruptedException{
leftChopstick.take();
rightChopstick.take();
}

//allows chopsticks to become usable again
public void setChopsticks(){
leftChopstick.drop();
rightChopstick.drop();
}
}