• Post Reply Bookmark Topic Watch Topic
  • New Topic

Thread Communication

 
rob mcfarland
Greenhorn
Posts: 11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm having a Threads nightmare. I've been trying all day to write some relatively simple
code whereby a Supply object places Integer objects in an ArrayList one at a time and
two Demand objects compete to remove them and process them. I've read the JLS and various
textbooks but I still can't work out why its not working as I think it should. If
anyone has the time and patience to explain to me whatever annoyingly simple mistake
I'm making, I would be eternally grateful. I've slimmed down the code as much as I can
but there's still quite a lot of it I'm afraid. Here goes:

Basically I thought that the first Demand object would start, succeed in getting a lock
on the ArrayList and then after finding it was empty, call wait and so relinquish the lock.
Meanwhile the second Demand object does exactly the same thing : gets the lock, finds its
empty and then calls wait. So the two Demand objects are both waiting on the same
object (ArrayList). Then the Supply object comes along, acquires the lock on ArrayList,
creates an Integer and adds it to it, then calls notifyAll to tell the Demand objects
about it. One of the Demand objects succeeds in reacquiring the lock and processes the
Integer. Now this is the bit I don't understand. When this Demand object leaves
the synchronized code block, the second one seems to come out of its wait state. Why??!!
I thought only a notify or notifyAll would cause a thread to leave its wait state. Needless
to say the Supply object hasn't supplied another Integer yet and the second Demand object
tries to access an empty ArrayList creating an Exception. Help....!
 
Jerry Pulley
Ranch Hand
Posts: 221
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Rob;
It may be annoying but consumer/supplier relationships are not simple. Some ideas (I haven't really tried them out, they're just based on observation):
1) Contending for a lock is a different state than waiting. <code>notifyAll</code> takes all threads out of the wait set and lets them contend for the lock. Looks like you only want to wake one <code>Demand</code> at a time, so try <code>notify</code> instead.
2) (Not certain about this one) Even after that, it's still possible for a <code>Demand</code> to wake and check <code>isEmpty</code>, then --timeslice-- the other <code>Demand</code> removes the object from the tray, then the first tries to access it - voila, exception. Try saying: "if empty then wait else get object" instead of "if empty then wait, then get" inside the <code>Demand</code> loop.
3) To ensure the program ends and doesn't just hang, don't remove the magic object. This'll let the other <code>Demand</code> see it as well so it can end too.
jply
P.S. I've got a producer/consumer I built when I was figuring this stuff out, too. Let me know if you want it.
 
mohit joshi
Ranch Hand
Posts: 243
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
with a little modification your code seems to run fine
I am posting the entire program as it is, for you to check. the only change I have made is to replace an if condition with a while loop when your demand thread is supposed to wake up.

Sorry I am removing the code, need to study the exact problem little more.
[This message has been edited by mohit joshi (edited October 13, 2000).]
 
mohit joshi
Ranch Hand
Posts: 243
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I Suppose this is what you were trying to achieve:

D:\jprac1>java Test
Main : finishing...
Demand 1 : Yes, I'm in for a wait
Demand 2 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 1 : ...finished waiting
Demand 1 : Boy, this Integer is heavy...
Demand 1 : Integer all processed. Ok, I'm gonna go for another one...
Demand 1 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 2 : ...finished waiting
Demand 2 : Boy, this Integer is heavy...
Demand 2 : Integer all processed. Ok, I'm gonna go for another one...
Demand 2 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 1 : ...finished waiting
Demand 1 : Boy, this Integer is heavy...
Demand 1 : Integer all processed. Ok, I'm gonna go for another one...
Demand 1 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 2 : ...finished waiting
Demand 2 : Boy, this Integer is heavy...
Demand 2 : Integer all processed. Ok, I'm gonna go for another one...
Demand 2 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 1 : ...finished waiting
Demand 1 : Boy, this Integer is heavy...
Demand 1 : Integer all processed. Ok, I'm gonna go for another one...
Demand 1 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 2 : ...finished waiting
Demand 2 : Boy, this Integer is heavy...
Demand 2 : Integer all processed. Ok, I'm gonna go for another one...
Demand 2 : Yes, I'm in for a wait
....
....
I have simplyfied your code because to objective was to see how threads wakeup on being notified. In perticular, I have done away with the (-1) Integer object, though I am ensuring that the supplier creates only one Integer and only when there is no Integer in tray.
Following is the modified code. Feel free to ask any further questions.
import java.util.*;
class Test{
static ArrayList tray = new ArrayList();
public static void main(String[] args){
Thread supply = new Thread(new Supply(tray, 5));
Thread demand1 = new Thread(new Demand(tray, "Demand 1 : "));
Thread demand2 = new Thread(new Demand(tray, "Demand 2 : "));
demand1.start();
demand2.start();
supply.start();
System.out.println("Main : finishing...");
}
}

class Supply implements Runnable{
ArrayList tray;
int numIntegers;
Supply(ArrayList tray, int numIntegers){
this.tray = tray;
this.numIntegers = numIntegers;
}
public void run(){
Integer temp = null;
while(true)
{
synchronized(tray){
//only one element is required in the tray
if(tray.size()==0){
// Takes a while to whistle up an Integer object
System.out.println("Supply : Starting construction of Integer number...");
try
{Thread.sleep((long)(Math.random() * 500.0));
}
catch(InterruptedException e){
System.out.println(e);
}
temp = new Integer((int) (Math.random() * 100));
tray.add(temp);
System.out.println("Supply : Finished construction of Integer number.");
tray.notify();
}//end of synchronized
// Give the other threads a chance to wake from their wait states
try{Thread.sleep(25);}
catch(InterruptedException e){}
}
}//end of for loop

}// end of run
}

class Demand implements Runnable{
// Class to process Integer objects placed on the Stack
String name;
// Name of demand object
ArrayList tray;
Demand(ArrayList tray, String name)
{this.tray = tray;this.name = name;
}
public void run()
{Integer val = null;
// Local variable to hold integer retrieved
boolean flag = true;
// Continue to run whilst there's Integers to be processed
while(true){
synchronized(tray){
while(tray.isEmpty()){
// Attempt to get exclusive access to tray
System.out.println(name + "Yes, I'm in for a wait");
// Check if its empty and wait if it is
try{tray.wait();
System.out.println(name + "...finished waiting");
}
catch(InterruptedException e){
System.out.println(e);
}
}//end of while supply off
}//end of synchronized
System.out.println(name + "Boy, this Integer is heavy...");
try
{
Thread.sleep((long)(Math.random() * 1000));
}
catch(InterruptedException e){
System.out.println(e);
}
// Remove Integer
val = (Integer) tray.remove(0);
System.out.println(name + "Integer all processed. Ok, I'm gonna go for another one...");
}//end of while true
}//end of run
}

[This message has been edited by mohit joshi (edited October 14, 2000).]
 
rob mcfarland
Greenhorn
Posts: 11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mohit,
Apologies for the delay in getting back to you. I had my SCJP exam yesterday so I had to
focus on that! I really appreciate you taking the time to look at the code - its starting
to drive me mad!! I do have a couple of questions/points regarding your version:
1) If you change the notify() to a notifyAll() in the Supply class, then you'll start
to see the same problem I was having where one of the Demand objects falls over with an
Exception as there aren't any Integers to fetch. This is what I don't understand. I
thought the only difference between notify and notifyAll was that notifyAll gave all
waiting threads the chance to reacquire the lock, but at the end of the day only one
will be successful which is the same result as notify. I can't see why making this
change means that one of the Demand threads starts falling over. A thread which wakes
up but fails to acquire the lock should go back to waiting and won't try again until
another notify is called. (ie another Integer has been built by Supply)
This doesn't seem to be happening. As soon as the thread
which did acquire the lock exits its synchronized block, the other thread wakes up
and goes to get an Integer which it then finds doesn't exist.
2) After changing the notify to a notifyAll, you can stop the thread from falling over
by moving the code which removes the Integer from the ArrayList back inside the
synchronized code loop. To be honest I think this is where this code should go anyway as the
thread should still be synchronized on the list until it has removed the Integer. Once
this has happened the Supply object can build another one and both threads can compete
to see who should remove it. However, the same thing is happening, ie as soon as the
thread that has removed the Integer exits its synchronized block, the other thread
wakes up from its waiting state and checks the List even though another notify hasn't
been called. You can see this in the output as the following statements will be
consecutive:
Demand 1 : Yes, I'm in for a wait- got lock on List but List is empty
Demand 1 : ...finished waiting- finished waiting (been woken up by other Thread exiting synchronized block)
Demand 1 : Yes, I'm in for a wait.- however, List is still empty as Supply hasn't created another Integer yet.
So in summary, I'm not sure I'm any closer to understanding the problem here! However,
between the two of us I'm confident we can crack it! I'd be very interested to hear your
thoughts on it.
Cheers,
Rob.
 
mohit joshi
Ranch Hand
Posts: 243
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think this is more like it:

Demand 1 : Yes, I'm in for a wait
Demand 2 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 1 : ...finished waiting
Demand 1 : Boy, this Integer is heavy...
Demand 1 : Integer all processed. Ok, I'm gonna go for another one...
Demand 2 : ...finished waiting
Demand 2 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 1 : Boy, this Integer is heavy...
Demand 1 : Integer all processed. Ok, I'm gonna go for another one...
Demand 2 : ...finished waiting
Demand 2 : Yes, I'm in for a wait
Supply : Starting construction of Integer number...
Supply : Finished construction of Integer number.
Demand 1 : Boy, this Integer is heavy...
Demand 1 : Integer all processed. Ok, I'm gonna go for another one...
Demand 2 : ...finished waiting
Demand 2 : Yes, I'm in for a wait
.......
code:
import java.util.*;
class Test{
static ArrayList tray = new ArrayList();
public static void main(String[] args){
Thread supply = new Thread(new Supply(tray, 5));
Thread demand1 = new Thread(new Demand(tray, "Demand 1 : "));
Thread demand2 = new Thread(new Demand(tray, "Demand 2 : "));
demand1.start();
demand2.start();
supply.start();
System.out.println("Main : finishing...");
}
}

class Supply implements Runnable{
ArrayList tray;
int numIntegers;
Supply(ArrayList tray, int numIntegers){
this.tray = tray;
this.numIntegers = numIntegers;
}
public void run(){
Integer temp = null;
while(true)
{
synchronized(tray){
//only one element is required in the tray
if(tray.size()==0){
// Takes a while to whistle up an Integer object
System.out.println("Supply : Starting construction of Integer number...");
try
{Thread.sleep(500);
}
catch(InterruptedException e){
System.out.println(e);
}
temp = new Integer((int) (Math.random() * 100));
tray.add(temp);
System.out.println("Supply : Finished construction of Integer number.");
tray.notifyAll();
}//end of synchronized
// Give the other threads a chance to wake from their wait states
try{Thread.sleep(25);}
catch(InterruptedException e){}
}
}//end of for loop

}// end of run
}

class Demand implements Runnable{
// Class to process Integer objects placed on the Stack
String name;
// Name of demand object
ArrayList tray;
Demand(ArrayList tray, String name)
{this.tray = tray;this.name = name;
}
public void run()
{Integer val = null;
// Local variable to hold integer retrieved
boolean flag = true;
// Continue to run whilst there's Integers to be processed
while(true){
synchronized(tray){
while(tray.isEmpty()){
// Attempt to get exclusive access to tray
System.out.println(name + "Yes, I'm in for a wait");
// Check if its empty and wait if it is
try{tray.wait();
System.out.println(name + "...finished waiting");
}
catch(InterruptedException e){
System.out.println(e);
}
}//end of while supply off

System.out.println(name + "Boy, this Integer is heavy...");
try
{
Thread.sleep(200);
}
catch(InterruptedException e){
System.out.println(e);
}
// Remove Integer
val = (Integer) tray.remove(0);
System.out.println(name + "Integer all processed. Ok, I'm gonna go for another one...");
}//end of synchronized(we want one thread to hold the lock while it is removing the Integer,
// so that the other thread is not able to get hold of the lock and goes back to sleep
}//end of while true
}//end of run
}
Here I have hardcoded the sleep period to make things clear. The noticable change I have made is to extend the scope of demand synchronized block, so that at a time only one thread can actually remove the Integer. So after notifyAll, ONE thread wakes up and locks the monitor till it has taken out the Integer.
when the other thread which was waiting but had received a notifyAll call finds that the monitor is Locked,so it wakes up and gets into ready state. (I suspect it again goes into wait state, but this time it is done by the synchronize mechanism and is transparent to the programmer,the synchronize mechanism works by using wait/notify in the background )so when the lock is released, our ready thread starts running ( I think it is implicitly notified by the synchronize mechanism ). However it finds that there is no integer for it to process so it goes back into waiting ( this time by explicit wait() that we have written).
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!