Java wait()不会被notify()唤醒

时间:2014-07-09 13:33:34

标签: java multithreading concurrency

你好我已经调试了一整天的代码了,但我不知道哪里出错了。

我在主线程上使用SerialPortEventListener,在工作线程中我有一个与服务器通信的客户端套接字。 由于在这个工作线程达到return之后,我仍然需要在主线程中完成一些总结工作,我想创建一个"伪线程"在主线程中等待,直到从侦听器onEvent方法通知它。

但是这个伪线似乎在永远等待。

我检查了锁定的线程pseudoThread,它们应该在Runnable和Listener类中具有相同的对象ID。

" PseudoThread等待"得到了显示,但PseudoThread清醒从未显示过。

  

控制台输出显示:   伪等待   ..   ..   虚假通知伪线。

PS如果我使用public final Object lock = new Object();在Main类中创建一个锁并用main.pseudoThread替换所有main.lock,我会得到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;
            }
        }

    }

}

在main方法中:

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.");
        }
}

3 个答案:

答案 0 :(得分:2)

如果不深入研究可能发生的事情,我可以看到你使用wait()/ notify()都是错误的。可能您正在经历失去的通知。"如果在调用它时没有线程等待它,则notify()函数 nothing 。如果你的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()时遵守上述所有三个规则,那么你的程序将更有可能按照你期望的方式运行。

答案 1 :(得分:1)

正如詹姆斯所建议的那样,它可能会丢失通知案例,或者可能是......两个线程1-你的主线程和2-伪线程正在等待相同的线程实例锁(main.pseudoThread)( Main线程通过调用join方法等待同一个锁)。 现在你使用的是从连接方法中唤醒主线程的通知,而不是那个 在你的Pseudo中等待。要检查第二种情况,请尝试在记录中调用notifyall 确认第二种情况或将统治这种可能性。

无论如何请重构你的代码,不要在Thread实例上使用synch这是不好的做法。去ReentrantLock或CoundDownLatch。

答案 2 :(得分:1)

notifywait的使用似乎不正确。方法名称通知可能有点误导,因为它不是用于通知“通知”。这些方法用于控制同步块的执行。当前线程暂停时,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);
   //...
}