简体   繁体   English

在同步块中使用wait()方法时,JVM是否在等待notify()时释放监视器?

[英]When using wait() method in a synchronized block does JVM release the monitor while waiting for notify()?

I have a thread that starts another thread which performs an action that will cause an event to fire after it runs. 我有一个线程启动另一个线程,该线程执行的操作将导致事件在运行后触发。 I need to capture that event (via an event listener) in the first thread and continue the rest of the work. 我需要在第一个线程中捕获该事件(通过事件侦听器),然后继续其余的工作。 My question is that while the first thread is waiting for the event listener to call notify() will it release the monitor? 我的问题是,当第一个线程正在等待事件侦听器调用notify()时,它将释放监视器吗? If not, how can I design this algorithm? 如果没有,我该如何设计该算法? Am I using the wait() and notify() methods correctly and for the correct lock (thread1)? 我是否正确地使用了wait()notify()方法并获得了正确的锁(线程1)?

Here is how the code looks like: 代码如下所示:

public class Thread1 {
    public void run() {
        Thread thread1 = Thread.currentThread();
        EventListener listener = new EventListener(thread1);
        Performer performer = new Performer();
        performer.addOnPerformedListener(listener);

        synchronized(thread1) {
            performer.run();    // Launches thread 2
            thread1.wait();
        }
        ...
    }

    public class EventListener implements Performer.OnPerformedListener {
        private Thread thread;

        public EventListener(Thread thread) {
            this.thread = thread;
        }

        @Override
        public void onPerformed() {
            synchronized (thread) {
                thread.notify();
            }
        }
    }
}

This is close. 这很近。 It's hard to tell how close without the rest of the code though (in performer). 但是,很难(在执行者中)知道没有其余代码有多接近。

First thing, I recommend not holding the lock on the thread over the " performer.run() " call - if that's feasible. 首先,我建议不要在“ performer.run() ”调用上保持线程的锁-如果可行的话。 Keeping critical sections (code executed with locks held) as small as possible is a "best practice". 尽可能使关键部分(保持锁的代码执行)尽可能小是“最佳实践”。

Second, I recommend using a condition flag set in the performer (if I'm understanding the code correctly) and performing the check inside the critical section before calling wait() - so that missed notification does not lead to a missed condition. 其次,我建议使用在执行者中设置的条件标志(如果我正确理解了代码),并在调用wait()之前在关键部分内执行检查-以便错过的通知不会导致错过的条件。

Here's psuedo-code: 这是伪代码:

THREAD1
* Initialize THREAD2
* Start THREAD2
* synchronized ( THREAD2 ) { while ( THREAD2.conditionNotMet() ) { THREAD2.wait(); } }

THREAD2
* Initialize "condition not met"
* Run
* On condition met, set "condition met", then synchronized ( THREAD2 ) { THREAD2.notifyAll(); }

Furthermore, if the condition may occur multiple times and the code waiting on the condition only wants to continue on newer occurrences, try a sequence number instead of just a "was condition met" (eg if ( myLastOccurrenceNumber < THREAD2.getLastOccurenceNumber ) { MET-CONDITION ) } else { WAIT } ). 此外,如果该条件可能多次出现,并且等待该条件的代码仅想在新出现的情况下继续,请尝试使用序列号,而不是仅“满足条件”(例如, if ( myLastOccurrenceNumber < THREAD2.getLastOccurenceNumber ) { MET-CONDITION ) } else { WAIT } )。

The basic idea of what you're doing is right. 您正在做什么的基本想法是正确的。 As the Javadoc of the Object#wait() method states, it releases the monitor that you acquired using the synchronized statement while it waits: Object#wait()方法的Javadoc所述,它在Object#wait()时释放您使用synchronized语句获取的监视器:

The current thread must own this object's monitor. 当前线程必须拥有该对象的监视器。 The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up 线程释放此监视器的所有权,并等待直到另一个线程通知等待在此对象的监视器上唤醒的线程

However, there is a reason why it is not guaranteed to work correctly in every situation as you have implemented it, and the reason is that the wait call can also wake up through "interrupts and spurious wakeups". 但是,有一个原因不能保证在您实施时在每种情况下都能正常工作,原因是wait调用也可以通过“中断和虚假唤醒”来唤醒。 (It will work correctly in 99% of the cases as you have currently implemented, which makes it all the more tricky as this gives a false sense that it will always work) (它可以在您当前已实施的99%的情况下正常工作,这使其变得更加棘手,因为这会误以为它会一直工作)

As the Javadoc states: 如Javadoc所述:

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop: 与一个参数版本中一样,可能会产生中断和虚假唤醒,并且应始终在循环中使用此方法:

 synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition } 

So you'll also need something to indicate the condition "performed" -the easiest way to do that is by creating a boolean field named performed that you set to true before calling notify in the onPerformed method. 所以,你还需要什么来表示“执行”的情况做-the最简单的方法是创建一个boolean名为场performed ,你设置为true调用之前notifyonPerformed方法。

The value of this flag will be seen correctly by thread1 because thread2 releases the monitor before thread1 acquires the same monitor, and this establishes a happens-before relationship as describes in the Java Memory Model specification. thread1将正确看到此标志的值,因为thread2thread1获取同一监视器之前释放了监视器,并且这建立了Java内存模型规范中所述的事前发生关系。

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

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