Java wait()不会被notify()唤醒
你好我已经调试了一整天的代码了,但我看不出哪里可能出错了。
我在主线程上使用SerialPortEventListener,在工作线程中我有一个与服务器通信的客户端套接字。 由于在这个工作线程return
,我仍然需要在主线程中完成一些总结工作,我想创建一个在主线程中等待的“伪线程”,直到从侦听器onEvent方法通知它。
但是这种伪线似乎永远等待着。
我检查了锁定的线程pseudoThread
,它们应该在Runnable和Listener类中具有相同的对象id。
显示“PseudoThread waiting”,但是从未显示PseudoThread清醒。
控制台输出显示:PseudoThread等待.. .. false通知伪线程。
PS如果我在Main类中使用public final Object lock = new Object();
创建一个锁public final Object lock = new Object();
并用main.pseudoThread
替换所有main.pseudoThread
,我得到java.lang.IllegalMonitorStateException。
private class Pseudo implements Runnable{ Main main; public Pseudo(Main main) { this.main = main; } @Override public void run() { synchronized(main.pseudoThread){ try { System.out.println("PseudoThread waiting"); main.pseudoThread.wait(); System.out.println("PseudoThread awake"); } catch (InterruptedException e) { e.printStackTrace(); return; } } } }
在主要方法:
public static void main(String[] args) { Main main = new Main(); main.initArduino(); //more code. including starting the working thread main.pseudoThread = new Thread(main.new Pseudo(main)); main.pseudoThread.start(); try { main.pseudoThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } private void initArduino() { arduino = new Arduino(this); if(!arduino.initialize()) System.exit(1); }
并在侦听器类中(也在主线程中运行)
//class constructor; public Arduino(Main Main){ this.main = Main; } //listening method public void serialEvent(SerialPortEvent oEvent){ //some code to interract with working thread. record(); } private void record(){ synchronized(main.pseudoThread){ main.pseudoThread.notify(); System.out.println("notified pseudothread."); } }
正如詹姆斯所建议的,它可能会丢失通知案例,或者可能是那样……两个线程1-你的主线程和2-伪线程正在等待相同的线程实例锁(main.pseudoThread)( 主线程等待同一个锁通过调用join方法 )。 现在你使用的是从join方法中唤醒Main线程的notify而不是在Pseudo中等待的那个。 要检查第二种情况,请尝试在记录中调用notifyall,这将确认第二种情况或将统治这种可能性。
无论如何请重构您的代码,不要在Thread实例上使用synch它的不良做法。 去ReentrantLock或CoundDownLatch。
如果不深入研究可能发生的事情,我可以看到你使用wait()/ notify()都是错误的。 可能你正在经历“丢失通知”。 如果在调用它时没有线程等待,则notify()函数不执行任何操作 。 如果你的serialEvent()函数在另一个线程调用wait()之前调用notify(),那么通知将会丢失。
考虑这个例子:
class WaitNotify() { private final Object lock = new Object(); private long head = 0; private long tail = 0; public void consumer() { synchronized (lock) { while(head == tail) { lock.wait(); } doSomething(); count head += 1; } } public void producer() { synchronized (lock) { tail += 1; lock.notify(); } } }
要点是:
(1)consumer()函数等待数据之间的某种关系变为true:这里等待head != tail
。
(2)consumer()函数在循环中等待。 这有两个原因:(a)许多程序都有多个消费者线程。 如果消费者A从wait()中醒来,则无法保证消费者B尚未声明他们都在等待的任何内容。 并且(b)即使没有调用foo.notify(),Java语言规范也允许foo.wait()有时返回。 这被称为“虚假的唤醒”。 允许虚假唤醒(只要它们不经常发生)使得实现JVM变得更容易。
(3)锁定对象是程序用来保护条件所依赖的变量的锁。 如果此示例是较大程序的一部分,则无论同步代码是wait()ing还是notify(),您都会看到围绕头部和尾部的每次使用的synchronized(lock)
。
如果您自己的代码在调用wait()和notify()时遵循上述所有三个规则,那么您的程序将更有可能按照您期望的方式运行。
使用notify
和wait
似乎不正确。 方法名称通知可能有点误导,因为它不是用于通知“通知”。 这些方法用于控制同步块的执行。 当前线程暂停时,Wait将允许其他一些线程与同一对象同步。 基本上,当某些资源不可用且执行无法继续时,将使用此方法。 另一方面,通知将在通知线程完成其同步块之后从等待中唤醒一个等待线程唤醒。 只有一个线程可以同时处于同一对象的同步块中。
如果想法只是保持主程序运行直到通知,那么信号量将更合适。 像这样的东西。
public void run() { System.out.println("PseudoThread waiting"); main.semaphore.acquireUninterruptibly(); System.out.println("PseudoThread awake"); } //... private void record(){ main.semaphore.release(); } //... public static void main(String[] args) { main.semaphore = new Semaphore(0); //... }