Ben_ said:
I agree that locking should be as short as possible. But, what is a short
interval and how long is an extended period of time ? I don't believe it
much relevant to give numbers, because when it comes to a situation where
many threads work on the same objects, a few milliseconds can become an
extended period of time... Time is a relative notion.
Anyway, actually, the point is to avoid thread deadlocks.
Wouldn't it be nice to code something like:
while (bStillWorkToDo) {
synchronize (max 30 ms) o {
o.doSomething();
} catch (SynchronizationTimeOutException e) {
// Don't waste more time waiting on o
doSomethingElse();
}
}
I admit Exception is not a nice approach because it's time consuming, but
you get the idea that I'd like to timeout rather than to block on a
synchronization.
The very root reason to this is that if the application enters in a thread
dead lock, it becomes completely unusable. I'd prefer to have the ability to
continue working and possible identify this situation to act (e.g. alarm).
While there is no support for testing whether or not an object is currently
locked by synchronization, it's not too hard to create a Mutex object that
can give you what you want. It would internally use Object.wait(long
timeout) to wait for the mutex. Example code follows. Note that it was
whipped up quickly and hasn't been thoroughly tested. There may be some
libraries out there that provide a similar class.
Note that this can be dangerous, because it is possible for a thread to
aquire() the mutex and forget to release() it. Remember to use finally
blocks to ensure that the mutex is released.
public class Mutex {
private LockImpl lockObject = new LockImpl();
public static interface Lock {
public void release();
}
private static class LockImpl implements Lock {
private boolean acquired = false;
private synchronized Lock acquire( long timeout )
throws InterruptedException {
if ( acquired ) {
wait( timeout );
}
if ( acquired ) {
// timeout expired
return null;
}
acquired = true;
return this;
}
public synchronized void release(){
acquired = false;
notifyAll();
}
}
public Lock acquire() throws InterruptedException {
return acquire( 0 );
}
// Returns null if timeout expired. Otherwise, returns
// a Lock instance which can be used to release the mutex.
public Lock acquire( long timeout ) throws InterruptedException {
return lockObject.acquire( timeout );
}
}
// Example usage
public class MutexExample {
public static void main( String[] args ) {
final Mutex mutex = new Mutex();
Thread testThread = new Thread() {
public void run() {
try {
Mutex.Lock lock = mutex.acquire();
try {
System.out.println( "Background thread: pretending "
+
"to do work by sleeping for 10 seconds." );
Thread.sleep( 10000 );
}
finally {
System.out.println( "Background thread: " +
"releasing mutex." );
lock.release();
}
}
catch ( InterruptedException e ) {
e.printStackTrace();
}
}
};
testThread.start();
try {
// pause to ensure the background thread acquires the mutex
first
Thread.sleep( 1000 );
System.out.println( "Main thread: Attempting to acquire mutex "
+
"with timeout of 2 seconds" );
Mutex.Lock lock = mutex.acquire( 2000 );
if ( lock == null ) {
System.out.println( "Main thread: lock acquisition
failed." );
}
else {
System.out.println( "Main thread: lock acquired." );
lock.release();
}
System.out.println( "Main thread: Attempting to acquire mutex "
+
"with timeout of 15 seconds" );
lock = mutex.acquire( 15000 );
if ( lock == null ) {
System.out.println( "Main thread: lock acquisition
failed." );
}
else {
System.out.println( "Main thread: lock acquired." );
lock.release();
}
}
catch ( InterruptedException e ) {
e.printStackTrace();
}
}
}