|
|
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 2 of 7
Be that as it may, while looking at my code to make sure Joe hadn't found a bug, I found a bug myself. (I'll show you the
code in a moment, but let's discuss the problems in it first.) The acquire() method was using a standard spin lock while waiting to acquire the mutex, but this strategy is appropriate only when the
timeout is infinitely large. I've fixed the problem by making the loop terminate for timeout values less than Integer.MAX_VALUE (the value I use for "forever"). It continues to use a spin lock in "forever" cases.
While I was at it, I also decided to have acquire() indicate whether or not it had timed out. The choices here are the usual ones: a return value or an exception toss. I opted
for the latter because a timeout typically is an error condition, and I didn't want to clutter up the code with unnecessary
tests for false return values.
I modified the definition for the Semaphore interface to incorporate this new exception:
01 | package com.holub.asynch;
02 |
03 | interface Semaphore
04 | {
05 | int id ();
06 | void acquire(long timeout) throws InterruptedException,
07 | Timed_out;
08 | void release();
09 |
10 | public static class Timed_out extends java.lang.RuntimeException
11 | { Timed_out(){ super("Timed out while waiting to acquire semaphore"); };
12 | }
13 | }
Note that Semaphore.Timed_out is a RuntimeException, so you don't have to catch it if the timeout is a fatal error (often the case).
The new (and this time, I hope, correct) version of acquire() now looks like this:
01 | public synchronized void acquire( long timeout ) throws InterruptedException
02 | {
03 | if( timeout == 0 ) // don't wait at all
04 | { acquire_without_blocking();
05 | }
06 | else if( timeout == Long.MAX_VALUE ) // wait forever
07 | { while( !acquire_without_blocking() )
08 | this.wait( timeout );
09 | }
10 | else // wait limited by timeout
11 | { if( !acquire_without_blocking() )
12 | { this.wait( timeout );
13 | if( !acquire_without_blocking() )
14 | throw new Semaphore.Timed_out();
15 | }
16 | }
17 | }
Finally, in last month's column, I inadvertently used an outdated version of the Lock_manager's comparator class. (It threw a Not_Comparable exception -- an artifact of my own sort implementation, which I abandoned when Java added an official one.) Anyway, the comparator class should look like this:
private static class Lock_comparator implements Comparator
{ public int compare(Object a, Object b)
{ return ((Semaphore)a).id() - ((Semaphore)b).id();
}
public boolean equals(Object obj)
{ return obj instanceof Lock_comparator;
}
}
I've modified the code in the "Goodies" section of my Web site (see Resources) to incorporate all these changes.
Now, on to the meat of this month's article.
Forging ahead -- to boldly split infinitives no one has split before:
I've brought up "condition variables" before in the context of wait() and notify(). The central concept is that a thread will wait until some condition becomes true. For example, a thread may need to wait
for somebody to push a button before proceeding with an action, or a thread may wait for something to appear in an empty queue
(for the queue-not-empty condition to become true).