Win a copy of Design for the Mind this week in the Design forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

wait and notify

 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This example is taken straight from a Java 2 revision for the programmer exam.

1. class ThreadA {
2. public static void main(String [] args) {
3. ThreadB b = new ThreadB();
4. b.start();
5.
6. synchronized(b) {
7. try {
8. System.out.println("Waiting for b to complete...");
9. b.wait();
10. } catch (InterruptedException e) {}
11. }
12. System.out.println("Total is: " + b.total);
13. } // end main
14.} // end Class
15.
16.class ThreadB extends Thread {
17. int total;
18.
19. public void run() {
20. synchronized(this) {
21. for(int i=0;i<100;i++) {
22. total += i;
23. }
24. notify();
25 }
26. }
27.}

Basically the main thread waits for b to do a calculation and prints result. The book explains that when b is started, main continues and gets lock on b, enters its synchronized block. Then wait() on b makes it release the lock and wait until b calls notify to tell it its done calculating. Main can then read the data when selected to run again by JVM, having regotten lock for b. And we're hunky dory.

BUT, what happens it b is lightning fast and its run method is called b4 main get lock on b and calls wait etc (ie b4 main gets to its synch block), which can happen as thread execution is indeterminate. b is in its synchronized block with lock on "this", doing the calculation so main cant enter its synch block.
b completes, and calls notify and exits its synch block. So main can now get lock on b and go into its synch block...but it then calls wait() on b expecting to be notified soon of a calculation completion. But b has exited its synch block earlier and notify() will never be called - is main stuck for ever waiting here???(obviously we could pass a timeout to wait() but that wasnt the case in the code in the book)

cheers
//Tom
 
Ernest Friedman-Hill
author and iconoclast
Marshal
Pie
Posts: 24208
35
Chrome Eclipse IDE Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, main() would be stuck forever waiting. This is called a "race condition", and it happens often in sloppy programs. It's one example of why programming with threads is hard. Congratulations for seeing that it could happen, though -- this means you've got what it takes to be good at it!

Another way to deal with this is to give ThreadB a boolean member variable "completed" which is set to true after the thread is done, and then have main() do something like



Because "completed" is used by multiple threads, it should always be read and updated in synchronized blocks.
 
Stan James
(instanceof Sidekick)
Ranch Hand
Posts: 8791
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Neat question, Tom, and it's exposing some things I don't know.

Would "volatile" be sufficient for a public boolean like "completed"? This looks to be exactly the situation described in what I've read about volatile.

Would join(b) have the same risk as wait() if b happens to finish first?
 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
b.join() in main would put the current thread (ie main) into a waiting state until the thread b completed its run method and died. In that sense main would only the lock on b after its calculation was completed aand b died and main could read the total.
Hence if the calculation is only done once (like in example) then i think b.join() will suffice.

In a more complex case this wont work:
Above b.join() method assumes the calculation is to be done only once ever and now b then dead, after exiting synch block and run. In a real case, I suppose b would repeat the calculation (say downloading stock data or something) via an infinite while loop in the run method.
If the situation arose which i described in my first post, main wouldnt actually get infinitely stuck, i think it would however miss the first calculation result, as it wasnt set up to be waiting when b called notify. So in this scenario of repeated b calculation, join() is no good as main will never run as theres an infinite while loop in b.

//Tom
 
Stan James
(instanceof Sidekick)
Ranch Hand
Posts: 8791
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Makes good sense. The run() as given does a finite loop and dies, so I thought of join(). If run() were to give periodic results ... ping a web site all day and tell you if the response time is over 2 seconds ... I'd go for some kind of pub-sub and not make A wait on B at all.
 
Ernest Friedman-Hill
author and iconoclast
Marshal
Pie
Posts: 24208
35
Chrome Eclipse IDE Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Gotta be careful here if you start to experiment. Note that the implementation of join() uses wait/notify on the Thread object itself, and so this program (which also calls Thread.notify() ) will interact with it. In practice, wait()ing on a Thread is quite rare, so this doesn't come up too often -- but it's a real issue.
 
Warren Dew
blacksmith
Ranch Hand
Posts: 1332
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stan James:

Would "volatile" be sufficient for a public boolean like "completed"? This looks to be exactly the situation described in what I've read about volatile.

It has been a while since I looked at the specifics, but I believe that with the Java memory model, "volatile" would not have sufficed prior to Java 1.5. I think this may have been fixed in Java 1.5. I may be thinking of "long"s, though, as they are less thread safe due to nonatomic reads and writes of the two halves of the long; or maybe it's that "volatile long"s haven't been fixed even in 1.5.

I always use explicit synchronization rather than relying on "volatile", myself.
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic