I have tried to understand the necessity to implement threads by using wait()
and notify()
when accessing shared resources or relying on their state.
I see that the idea is to monitor objects and to wait for their availability and releasing them after use to make them usable for other threads/methods, but why are those methods necessary instead of just declaring the relevant objects as static volatile so that other threads learn about the change of state without invoking those methods?
For example
In a restaurant, there are 2 chefs. One of the chefs is a good chef (better cooking qualities, ..) and carries a boolean isGoodCook = true
, while the second chef is a bad chef and carries a boolean isGoodCook = false
.
There is only equipment for one chef to cook meals at a time. The bad chef cooks for a specific amount of time (= cookingTime) while the good chef comes into the kitchen occasionally to take over the bad chef's task of cooking meals. The good chef can never be interrupted in his cooking process and cooks for his entire cookingTime once he started.
(Bad chef stops cooking as long as the good chef takes the part of cooking meals (= cookingTime of good chef)).
And after the good chef stops cooking meals, the bad chef has to carry on the task of preparing meals once again.
private boolean cooking; //is equipment used at the moment
private boolean isGoodCook;
private boolean cookingDesire; //indicating to chef to stop cooking
private int cookingTime;
public CookingTask(boolean isGoodCook, int cookingTime)
{
this.isGoodCook = isGoodCook;
this.cookingTime = cookingTime;
}
public void run()
{
if(isGoodCook)
{
//notify actual cook to stop cooking
cookingDesire = true;
}
//wait til equipment to cook
//is available
while(cooking)
{
try
{
wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
//reserve equipment
cooking = true;
cookingDesire = false;
//notify other threads (= bad cook)
notifyAll();
startCooking();
}
private void startCooking()
{
for(int i = 0; i < cookingTime; cookingTime--)
{
try
{
Thread.sleep(1000);
//if good chef comes in
if(cookingDesire)
{
//bad chef starts to pause
startBreak();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
cooking = false;
}
public void startBreak()
{
//bad chef stops cooking
cooking = false;
notifyAll();
//measure break time of bad chef
long stopCookingTime = System.currentTimeMillis();
while(cookingTime > 0 && cooking)
{
try
{
wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
int pausedTime = toIntExact((System.currentTimeMillis() - stopCookingTime)/1000);
//calculate remaining cookingTime
cookingTime -= pausedTime;
cooking = true;
notifyAll();
}
Maybe someone has the time to read through and shortly outline my misconceptions on monitoring/wait()
and notify()
on multiple threads, I'd gladly appreciate it!