2014-08-31 47 views
1

我有一个线程启动另一个线程执行一个动作,它会导致事件在运行后触发。我需要在第一个线程中捕获该事件(通过事件侦听器)并继续其余的工作。我的问题是,当第一个线程正在等待事件监听器调用notify()时,它是否会释放监视器?如果不是,我该如何设计这个算法?我是否正确使用wait()notify()方法以及正确的锁定(thread1)?在同步块中使用wait()方法时,JVM会在等待notify()时释放监视器吗?

这是怎样的代码看起来像:

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

回答

0

你在做什么的基本思想是正确的。作为Object#wait()方法状态的Javadoc,会释放出您获得使用​​语句在等待监视器:

当前线程必须拥有该对象的监视器。该线程释放这个监视器的 所有权并等待另一个线程通知 线程在等待这个对象监视器上醒来

但是,为什么它不能保证你一定要在任何情况下正常工作的理由实现它,原因是wait调用也可以通过“中断和虚假唤醒”唤醒。(这将在99%的情况正常运行作为当前已实现,这使得它更加棘手,因为这给人一种错觉,以为它会一直工作)

正如Javadoc:

正如一个参数的版本,中断和虚假唤醒是 可能的,而且这种方法应该始终在循环中使用:

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

所以你还需要有些东西表示“执行”的条件 - 最简单的方法是创建一个名为performed的域boolean,您在onPerformed方法中调用notify之前设置为true

该标志的值将正确地thread1因为thread2释放监视器可以看出thread1获取相同的显示器之前,并且该建立之前发生的关系如在Java内存模型规范描述。

0

这接近。尽管(在表演者中)没有剩下的代码很难说有多接近。

首先,我建议不要在“performer.run()”调用上保持线程锁定 - 如果可行的话。保持关键部分(保持锁执行的代码)尽可能小是“最佳实践”。其次,我建议在执行者中使用条件标志(如果我正确理解代码)并在调用wait()之前执行关键部分内的检查 - 以便错过的通知不会导致错过条件。

这里是伪代码:

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

此外,如果可能会出现病情多次,等待只希望继续就新出现的条件码,可试用序列号,而不是只是一个“为条件符合“(例如if (myLastOccurrenceNumber < THREAD2.getLastOccurenceNumber) { MET-CONDITION) } else { WAIT })。

+0

下面是一个javadoc参考,也可以帮助:http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait() – ash 2014-08-31 04:06:48

+0

感谢您的答案。我首先包含了一个条件标志,但是之后根据在执行操作之前条件检查也会发生并且该标志设置为true的原因将其删除。但是,我发现它有一些安全价值。 – exbuddha 2014-08-31 16:45:22

+0

我不会认为它是“安全”。相反,请考虑所有可能的操作顺序。我使用的方法是遍历代码,一次一个部分,并考虑如果代码停在那里会发生什么,而其他竞争线程同时执行竞争代码。例如,启动另一个线程然后等待通知,如果另一个线程在第一次进入等待呼叫之前通知会发生什么情况?错过了通知。对第二个线程的整个操作的锁定太粗糙,甚至引发了另一个线程的问题。 hth – ash 2014-08-31 17:54:37

相关问题