Rotem B Rotem B - 1 month ago 5
Java Question

Locks and concurrency in java

I am new to java concurrency and working with locks.
I am trying to solve the dining problem and I don't have deadlocks yet only one thread gets actual runtime.
Can anyone tell me what am I doing wrong?

class Philosopher implements Runnable {

private Random numGenerator = new Random();

private int id;

private ChineseStick minChopstick;
private ChineseStick maxChopstick;

public Philosopher (int id, ChineseStick leftChopstick, ChineseStick rightChopstick) {
this.id = id;
if(leftChopstick.getNumber() > rightChopstick.getNumber()){
this.minChopstick = rightChopstick;
this.maxChopstick = leftChopstick;
}
else{
this.minChopstick = leftChopstick;
this.maxChopstick = rightChopstick;
}
}

/**
* Repeatedly think, pick up chopsticks, eat and put down chopsticks
*/
public void run() {
try {
while (true) {
pickUpLeftChopstick();
pickUpRightChopstick();
eat();
putDownChopsticks();
think();
}
} catch (InterruptedException e) {
System.out.println("Philosopher " + id + " was interrupted.\n");
}
catch(Exception e){
System.out.println("Philosopher " + id + " raised exceptions.\n");
}
}

/**
* Lets a random amount of time pass to model thinking.
* @throws InterruptedException
*/
private void think() throws InterruptedException {
System.out.println("Philosopher " + id + " is thinking.\n");
System.out.flush();
Thread.sleep (numGenerator.nextInt(10));
}


/**
* Locks the left chopstick to signify that this philosopher is holding it
* @throws InterruptedException
*/
private void pickUpLeftChopstick() throws InterruptedException {

while(!minChopstick.lock.tryLock()){
synchronized(minChopstick.lock){
minChopstick.lock.wait();
}
}
minChopstick.lock.lock();
System.out.println("Philosopher " + id + " is holding " + this.minChopstick.getNumber() + " chopstick.\n");
System.out.flush();
}

/**
* Locks the right chopstick to signify that this philosopher is holding it
* @throws InterruptedException
*/
private void pickUpRightChopstick() throws InterruptedException {
while(!maxChopstick.lock.tryLock()){
synchronized(maxChopstick.lock){
maxChopstick.lock.wait();
}
}
maxChopstick.lock.lock();
System.out.println("Philosopher " + id + " is holding " + this.maxChopstick.getNumber() + " chopstick.\n");
System.out.flush();
}

/**
* Lets a random amount of time pass to model eating.
* @throws InterruptedException
*/
private void eat() throws InterruptedException {
System.out.println("Philosopher " + id + " is eating.\n");
System.out.flush();
Thread.sleep (numGenerator.nextInt(10));
}

/**
* Releases the locks on both chopsticks to model putting them down so the
* other philosophers can use them.
*/
private void putDownChopsticks() {
minChopstick.lock.unlock();
synchronized(minChopstick.lock){
minChopstick.lock.notifyAll();
}
maxChopstick.lock.unlock();
synchronized(maxChopstick.lock){
maxChopstick.lock.notifyAll();
}
}



}


The stick class:

public class ChineseStick {

public ChineseStick(int number){
this.lock = new ReentrantLock();
this.number = number;
}

public Lock lock;

private int number;

public int getNumber(){
return this.number;
}

}


The Main is very simple:

public static void main (String[] args) {
// Model each chopstick with a lock
//Lock[] chopsticks = new ReentrantLock[NUM_PHILOSOPHERS];
ChineseStick[] sticks = new ChineseStick[NUM_PHILOSOPHERS];

for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
sticks[i] = new ChineseStick(i);
}

// Create the philosophers and start each running in its own thread.
Philosopher[] philosophers = new Philosopher[NUM_PHILOSOPHERS];

for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
philosophers[i] = new Philosopher(i, sticks[i], sticks[(i+1)%NUM_PHILOSOPHERS]);
new Thread(philosophers[i]).start();
}
}

Answer

You are trying to get the lock inside this loop: while(!maxChopstick.lock.tryLock()) …

This loop ends when tryLock returns true which implies that you have the lock. But you invoke lock additionally after the loop thus have locked twice. So a single unlock invocation does not release the lock then.

But your code contains other problems as well. You are swapping the left and right sticks in the Philosopher’s constructor based on the stick numbers which makes no sense as the main method already provides the stick instances correctly for a round table. It is correct for the last person to have a left item with a higher number than the right item as you wrap-around the array combining the last item with the first one. Swapping them then breaks what was initially correct.

Once you have fixed that, you will have the same problem as explained in this answer: If all eaters grab the left stick first, you can reach the situation that all eaters have locked their left stick and no one can proceed.

Additional coding style odds: you have a ChineseStick class but instead of providing a grab operation there, you implement it in the Philosopher with duplicate code for left and right sticks. If ChineseStick had a single pickUp() method, the Philosopher needed only invoke minChopstick.pickUp() and maxChopstick.pickUp() respectively rather than implementing two pickUp…Chopstick() methods containing the same code.

And you are mixing up Lock and synchronized. This is a perfect way to create unmaintainable, confusing code.

Comments