简体   繁体   English

Java线程处理:在lock.wait()中提供超时参数时发生意外行为

[英]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 作业系统:OS X 10.8.5

JDK: 1.6.0, 1.7.0.45 and 1.7.0.67 JDK:1.6.0、1.7.0.45和1.7.0.67

Your example does not show a while() loop around the wait() call. 您的示例未显示在wait()调用周围的while()循环。 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(); [编辑:添加了这一段,感谢内森·休斯(Nathan Hughes)提醒我...]调用循环处于循环中,因为在通知了锁之后,wait()ing线程仍必须重新获取该锁:线程A等待条件变为真,线程B使条件变为真并调用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. 不能保证线程C不会首先获得该锁,并且在wait()调用能够返回之前再次使条件变为假。

Also, wait() is allowed to return even when the object has not been notified (that's called a "spurious wakeup"). 同样,即使没有通知对象,也允许wait()返回(这称为“虚假唤醒”)。

The condition-to-be-waited-for is explicit in the code (ie, count > 0). 在代码中明确要等待的条件(即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. 除了在用于wait()和notify()调用的同一锁对象上同步时,什么都没有改变要等待的条件。

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. 不管您是否提供超时,对象上的wait方法都将释放当前线程在对象上持有的锁(如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. 使用您提供的代码并基于对场景的描述,我想是在执行lock.wait(60000L)的那一刻,JVM释放了对象上的锁,同时可能拾取处于运行/运行状态的任何其他线程如果它们在同一个对象上进行同步,则它们可能在通知程序线程获得该锁定之前就获得了锁定。

This behaviour is difficult to debug as it depends on JVM profiler to pick which thread should be run. 此行为很难调试,因为它取决于JVM分析器来选择应运行的线程。 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 . 因此,正如您在执行lock.wait(60000L)时所解释的那样,不必总是使通知程序线程单独承担对公共对象的锁定。 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. 如果还有其他线程也在公共对象上等待,那么它很可能最终获得锁定,从而导致通知程序线程无法获得锁定,因此lock.wait(60000L)超时。

Whenever you use lock.wait(..) you have to use the lock.notify() or lock.notifyAll() . 每当使用lock.wait(..)时 ,都必须使用lock.notify()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 这里是有关其用法的一些指南,我希望它有用: http : //www.javamex.com/tutorials/wait_notify_how_to.shtml

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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