简体   繁体   中英

Java Threading: Unexpected behavior when providing timeout argument in lock.wait()

Unfortunately I'm not going to be able to give full context to this, since there's too much complexity in the surrounding code. The short of it is this:

I have a block of code that's waiting on a lock:

            synchronized (lock) {
                lock.wait();
            }

Which works as expected. Fairly straightforward -- it acquires the lock, releases it when it starts waiting, another thread acquires the lock and then notifies on it.

However, as soon as I provide a timeout, the behavior changes entirely.

            synchronized (lock) {
                lock.wait(60000L);
            }

Again, should be fairly straightforward (and this works as expected in several other places in the code). However, in this one case, execution basically halts until the timeout occurs. My only guess as to what seems to be happening is it's not releasing the lock when it enters the wait -- the notifier is never able to acquire the lock, so the wait sleeps until it times out. And even worse, it's a blocking sleep -- no other threads are able to wait on the lock and it forces the execution to be entirely synchronous.

Anyone have any ideas as to what might be happening here? It's a fairly simple function and there's nothing weird going on with nested synchronization blocks at any point. Considering that by providing no timeout it should wait indefinitely, if the notifier itself was broken the code would be hanging forever, but that's not the case. It only stops working once the timeout is provided.

Any thoughts would be greatly appreciated.

OS: OS X 10.8.5

JDK: 1.6.0, 1.7.0.45 and 1.7.0.67

Your example does not show a while() loop around the wait() call. That suggests that you may not completely understand the use case for wait and notify. Here's one example:

// This object is used to synchronize *EVERY* method
// that can change the value of count.
final Object lock = new Object();

int count;

void waiter() {
    synchronized(lock) {
        while(count <= 0) {
            lock.wait();
        }
        //do something that you are only allowed to do
        //when count > 0.
    }
}

void notifier() {
    synchronized(lock) {
        count++;
        if (count >= 0) {
            lock.notify();
        }
    }
}

[Edit: Added this paragraph, thank's Nathan Hughes for reminding me that...] The wait() call is in a loop because the wait()ing thread still has to re-acquire the lock after the lock has been notified: if thread A is waiting for the condition to become true, and thread B makes the condition true and calls notify(); there's no guarantee that thread C won't get the lock first, and make the condition false again before the wait() call is able to return.

Also, wait() is allowed to return even when the object has not been notified (that's called a "spurious wakeup").

The condition-to-be-waited-for is explicit in the code (ie, count > 0).

Nothing changes the condition-to-be-waited-for except when synchronized on the same lock object that is used for wait() and notify() calls.

Irrespective of whether you provide a timeout or not the wait method on an object releases the lock held on the object by the current thread as commented by John.

With the code that you have given and based on your description of scenario my guess is that the moment lock.wait(60000L) is executed JVM releases the lock on the object meanwhile any other thread which is in runnable/running state might be picked up and if they are synchronizing on the same object then they might take the lock before your notifier thread would take the lock.

This behaviour is difficult to debug as it depends on JVM profiler to pick which thread should be run. So as you explained just when your lock.wait(60000L) is executed it need not always be that the notifier thread alone should pick up the lock on the common object . If there is any other thread which is also waiting on the common object it can very well get the lock finally leading to notifier thread not being able to get the lock and hence the lock.wait(60000L) gets timedout.

Whenever you use lock.wait(..) you have to use the lock.notify() or lock.notifyAll() . Make sure you use that where it makes sense in your logic and it will 'wake up' the lock before the timeout (considering the timeout value you put is enough). Here it's some guide for its usage, i hope its useful: http://www.javamex.com/tutorials/wait_notify_how_to.shtml

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM