Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 7
Fix the problem by replacing the if statement with a while loop (called a spin lock). Now the threads that don't get the object will go back to waiting:
public Object synchronized dequeue( )
{
try
{ while( head == tail ) // used to be an if
this.wait();
}
catch( InterruptedException e )
{ return null;
}
return queue[++tail %= queue_size ];
}
That while loop also solves another, less obvious problem. What if we leave the notify() statement in place, and don't put in a notifyAll()? Since notify() releases only one waiting thread, won't that solve the problem? It turns out that the answer is no. Here's what can happen:
notify() is called by the enqueueing thread, releasing the condition variable.dequeue() at exactly this point, and successfully enters the monitor because no other threads are officially waiting. This second thread successfully dequeues the object; wait() is never called since the queue isn't empty.This second scenario is easily fixed the same way as the first: replace the if statement with a while loop.
Note that the Java specification does not require that wait() be implemented as an atomic operation (that is, one that can't be preempted while moving from the condition variable to the
monitor's lock). This means that using an if statement instead of a while loop might work in some Java implementations, but the behavior is really undefined. Using a spin lock instead of a simple
if statement is cheap insurance against implementations that don't treat wait() as atomic.
Now let's move on to harder-to-find problems. The first difficulty is the commonplace confusion of threads and objects: Methods run on threads, objects do not. Put another way, the only way to get a method to run on a given thread is to call it (either directly or indirectly) from
that thread's run() method. Simply putting it into the Thread derivative is not enough. For example, look at the following simple thread (which just prints its fields every so often):
class My_thread extends Thread
{
private int field_1 = 0;
private int field_2 = 0;
public void run()
{
setDaemon(true); // this thread will not keep the app alive
while( true )
{
System.out.println( " field_1=" + field_1
" field_2=" + field_2 );
sleep(100);
}
}
synchronized public void modify( int new_value )
{ field_1 = new_value;
field_2 = new_value;
}
}
You could start up the thread and send it a message like this:
My_thread test = new My_thread; test.start(); //... test.modify(1);
The only functions that run on the new thread are run() itself and println() (which run() calls). The modify() method never runs on the same thread as the println call; rather, it runs on whatever thread was running when the call was made. (In this case, it runs on whatever thread main() is running on.) Depending on timing, the earlier fragment could print: