How to not synchronize on an object already locked ?

B

Ben_

Hello,

Basically, the idea is to avoid synchronizing on an object already locked,
to continue doing usefull instead of waiting.

For example, an object would be locked for an extended period of time and
the application would continue doing some usefull work aside, rather than
simply waiting on the lock, by checking some condition on the object.

How is this to be implemented ? Any tip appreciated.

Thx.
 
E

Eric Sosman

Ben_ said:
Hello,

Basically, the idea is to avoid synchronizing on an object already locked,
to continue doing usefull instead of waiting.

For example, an object would be locked for an extended period of time and
the application would continue doing some usefull work aside, rather than
simply waiting on the lock, by checking some condition on the object.

How is this to be implemented ? Any tip appreciated.

I'd suggest a redesign. A lock is not intended to be
held "for an extended period of time," but for the briefest
possible period of time. Use locks to prevent an object
from being manipulated while its state might be incoherent,
not to express part of the object's state.

Can you recast the question? Rather than asking "Is
this object locked," can you instead ask "Is this object
available for such-and-such use?" If so, you can express
the "available" aspect with a member of the object, and
you need only synchronize for the few heartbeats needed
to query and possibly change the "available" flag, not for
the entire duration of the suchAndSuch() method.

class MyClass {
boolean available = true;

synchronized boolean acquire() {
boolean was_available = available;
available = false;
return was_available;
}

synchronized release() {
available = true;
}

void suchAndSuch() {
/* time-consuming code goes here */
}
}

In effect, this uses the available flag as a sort of
substitute lock: You call acquire() to try to "lock" it and
learn of your success or failure, and you promise not to
call suchAndSuch() unless you've succeeded. When you're
through, you "unlock" by calling release().

Still, a redesign would probably be better. The "promise"
is not enforceable by the JVM, and sooner or later somebody
will make a mistake -- calling release() too soon, perhaps, or
forgetting to test the value returned by acquire(). It'd be
better to arrange things so that such errors are hard to make.
Maybe all the MyClass objects should live in a Collection, with
the drill being that objects are removed from the Collection
while they're in use and returned to the Collection when they
become available again. Then you need only synchronize on the
Collection while moving objects in and out, and you needn't hold
long-lived locks (real or imitation) on the objects themselves.
Other patterns are possible; what are you trying to do?
 
B

Ben_

Thanks for the extensive response. I appreciate.

I'm aware of locking on instance variable rather on the complete object to
reduce concurrency, but when the object to lock on is simple (I mean: it
hasn't that many variables), it would be nearly the same to lock the object
than its instance variable, wouldn't it.

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).

Thanks.
 
R

Roedy Green

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();
}

The problem is stopping. You can't just stop a task in mid
instruction and expect it to leave the world in a sensible state. The
stopping task has to put ITSELF to bed gracefully on request.

See StoppableThread as part of http://mindprod.com/products.html#BUS
 
D

David Hilsee

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();
}
}
}
 
T

Thomas Weidenfeller

Eric said:
I'd suggest a redesign. A lock is not intended to be
held "for an extended period of time," but for the briefest
possible period of time.

I don't agree here in general. Aquiring and releasing a lock costs CPU
cycles. If you try to build an MT-hot appliction by micro-optimizing all
access to critical sections to hold a lock the shortest time possible
you can end up with a large ammount of aquire/release operations burning
your performance.

It is often better to "finish what you have started" instead of
repeatedly aquiring and releasing a lock just because some parts of a
method don't need to be protected. That said, if you have determined you
absolutely need a lock you should still try to keep that one lock as
short as possible, but also try to do everything you have to do under a
lock in that lock.

/Thomas
 
E

Eric Sosman

Thomas said:
I don't agree here in general. Aquiring and releasing a lock costs CPU
cycles. If you try to build an MT-hot appliction by micro-optimizing all
access to critical sections to hold a lock the shortest time possible
you can end up with a large ammount of aquire/release operations burning
your performance.

It is often better to "finish what you have started" instead of
repeatedly aquiring and releasing a lock just because some parts of a
method don't need to be protected. That said, if you have determined you
absolutely need a lock you should still try to keep that one lock as
short as possible, but also try to do everything you have to do under a
lock in that lock.

"All generalizations are false."

You are quite right, and perhaps instead of "briefest possible"
I should have written "briefest practical." The objection I was
trying to raise was to the "extended period" the O.P. mentioned;
I envisaged some horrid model of a lending library in which each
book is locked when checked out and unlocked when returned three
weeks later ...
 

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,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top