why "synchronized" with wait/notify ?

A

anita

I know the language requires it and what exaclty happens with
wait/notify.

BUT it appears that the whole wait notify protocol should work just as
well without acquiring a monitor lock on the target object...

I've read something about why we need to have a thread own a monitor on
the object (key) that is waiting on, something about race condition.
but icant wrap my head around it, could someone care to walk me through
it please using a scenario where it would not work as expected.

So why would this not work (logic- forget the illegal monitor state
execption)
ThreadA
|
|
key.wait() //suspend threadA and add to waitset of key


ThreadB
|
|
key.notifyAlll; //Wake up one of the threads in the waitset of key



Thanks
 
E

Eric Sosman

anita wrote On 01/09/06 15:47,:
I know the language requires it and what exaclty happens with
wait/notify.

BUT it appears that the whole wait notify protocol should work just as
well without acquiring a monitor lock on the target object...

I've read something about why we need to have a thread own a monitor on
the object (key) that is waiting on, something about race condition.
but icant wrap my head around it, could someone care to walk me through
it please using a scenario where it would not work as expected.

So why would this not work (logic- forget the illegal monitor state
execption)
ThreadA
|
|
key.wait() //suspend threadA and add to waitset of key


ThreadB
|
|
key.notifyAlll; //Wake up one of the threads in the waitset of key

Let's draw the diagram a little differently:

ThreadA ThreadB
| |
| |
| key.notifyall()
| |
key.wait() |
|
:

This is known as the "lost wakeup problem."
 
C

Chris Smith

Eric Sosman said:
Let's draw the diagram a little differently:

ThreadA ThreadB
| |
| |
| key.notifyall()
| |
key.wait() |
|
:

This is known as the "lost wakeup problem."

I'll expand a bit further (though this is getting more complicated, and
Eric's response is good enough... so ignore me if you like). Here's the
problem.

wait() isn't strictly guaranteed to do anything at all. Something
called "spurious wakeups" might occur. That is, a call to wait() can
return at any time without any good reason. So a good rule of thumb for
using wait() is that you should consider it to be nothing but an
optimization. If a program issn't correct already (and assuming that
thread starvation is not occurring), calling wait() can NOT make a
program correct. However, it certainly can make a program incorrect!

So we need to start out thinking of how we'd write the code without wait
at all. So how do you you wait for something is wait() doesn't do
anything? Easy... you do a busy-wait:

while (!thingImWaitingFor()) /* DO NOTHING */;

This uses 100% of the CPU power available, of course, and performance
horribly. The optimization is this:

while (!thingImWaitingFor()) wait();

That explains the need for a "predicate", which in this case is
!thingImWaitingFor(). Next, you need an absolute guarantee that the
waiter and the notifier agree about the state of the predicate. The
waiter checks the state of the predicate at some point slightly BEFORE
it goes to sleep, but it depends for correctness on the predicate being
true WHEN it goes to sleep. There's a period of vulnerability between
those two events, which can break the program.

You need that period of vulnerability to be protected by a synchronized
block. If the waiter didn't synchronize, then any old bit of code might
change the predicate just before it goes to sleep, and then we're
certainly in trouble. If the notifier didn't synchronize, then it could
change the predicate even though the waiter is holding the lock... and
we'd still have trouble.

However, the waiter can't hold a synchronized lock while it's waiting.
At first glance, this problem seems impossible. Now, there's actually a
bit of magic involved here. The wait() method ATOMICALLY releases the
lock and goes to sleep. It's a sort of deus ex machina solution (deus
== Java Virtual Machine) to the otherwise impossible problem.

Note that this is not specific to Java. The same magic is necessary
everywhere. Here's some bit of sample code in C from W. Richard
Stevens' UNIX Network Programming Volume 2: IPC (slightly modified for
formatting and to fix typos in my rather old edition of the book).

void *consume(void *arg)
{
int i;
for (i = 0; i < nitems; i++)
{
pthread_mutex_lock(&nready.mutex);
while (nready.nready == 0)
{
pthread_cond_wait(&nready.cond, &nready.mutex);
}
nready.nready--;
pthread_mutex_unlock(&nready.mutex);

/* ... */
}

return NULL;
}

Note that, just like in Java, the call to wait (pthread_cond_wait)
involves both something to wait on (a condition variable in pthreads)
and a lock that must be held (a mutex in pthreads) which will be
atomically released when the originating thread waits.

There are alternatives, of course. IIRC, Win32's "events" lack (or, at
one time, lacked) this capability, but compensate by make the event
signal (roughly like a notify or notifyAll) persistent, so that the
event actually keeps track of any missed signals and stores them to
deliver later.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top