Java wait/notify - 没有唤醒线程

Java wait/notify - not waking up thread

我正在尝试做一个小练习来适应 wait/notify。 我想做的是简单地启动一个线程,然后通过 wait 使其进入睡眠状态,并通过 notify 多次将其唤醒。

我的代码是:

public class Simple{
    static final Thread mainThread = Thread.currentThread();

    public static void main(String[] args) throws InterruptedException {
        PrintThread printer = new PrintThread(0);
        printer.start();

        synchronized (mainThread){
            System.out.println("main sleeping while waiting for printer to be started");
            mainThread.wait();
            System.out.println("main woke up");


            for (int i = 0; i < 1000; i++) {
                synchronized (printer){
                    System.out.println("added num "+i);
                    printer.numToPrint = i;
                    System.out.println("main waking up printer");
                    printer.notifyAll();
                    System.out.println("main sleeping");
                    mainThread.wait();
                    System.out.println("main woke up");
                }
            }

        }

    }
}

class PrintThread extends Thread{
    public int numToPrint = -1;

    public PrintThread(int numToPrint){
        this.numToPrint = numToPrint;
    }

    @Override
    public synchronized void run() {
        System.out.println("printer started");
        while (true){
            try {
                synchronized (Simple.mainThread){
                    System.out.println("printer waking up main");
                    Simple.mainThread.notifyAll();
                }
                System.out.println("printer sleeping");
                wait();
                System.out.println("printer woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("printing num "+numToPrint);
        }
    }

}

我希望这会像

main sleeping while waiting for printer to be started
printer started
printer waking up main
printer sleeping
main woke up
added num 0
main waking up printer
main sleeping
printer woke up
printing num 0
printer waking up main
printer sleeping
main woke up
added num 1
...

而是这样:

main sleeping while waiting for printer to be started
printer started
printer waking up main
printer sleeping
main woke up
added num 0
main waking up printer
main sleeping

所以...通知似乎没有唤醒打印机线程?

这不应该是死锁,因为通过等待我释放了我所有的锁,所以 main 不应该对 printer[ 有任何锁定=32=] 和 打印机 应该可以唤醒并打印。

我做错了什么?

很可能在 Print 再次调用 wait() 之前调用了 notifyAll() 调用。问题是您对 wait 和 notifyAll 调用的依赖完全按照您希望的顺序进行。这是两个不同的执行线程,所以当然这不能保证,因此你得到了你所得到的。

实现此目的的更好方法是创建一个公共的第 3 个共享对象,两个线程都可以在该对象上获取锁。这将同步两个线程,因为它们等待访问此对象。

此外,您应该阅读 Thread.wait、notify 和 notifyAll 的 Javadoc。 If/when 你这样做,你会发现你永远不应该在线程上调用这些方法,因为它们用于执行 thread.join (不仅如此,我的 "claim to fame" 是我相信这是我的几年前的 bug 请求记录这个,当时它不在 JavaDoc 中导致它被添加到 Javadoc。可能是其他人,但它发生在我要求它之后:))

属性 : 调用 wait() 释放锁(它正在监视)并进入等待状态。它在同一对象上等待 notify() 或 notifyAll()。一旦 notify() 或 notifyAll() 并在 CPU 中被调度,它会在恢复之前再次获取锁。

当您在 main 方法中首先执行 "synchronized (mainThread)" 时,它基本上锁定了 "mainThread" class 对象。当调用 mainThread.wait() 时,mainThread 进入等待状态(等待有人调用 mainThread class 对象上的 notify 或 notifyAll)。

此时 PrintThread 可能会得到 CPU。这是 "synchronized (Simple.mainThread)" 被调度并锁定 "Simple.mainThread" 并通知所有等待 "Simple.mainThread" 的线程的时候。在此块完成后,PrintThread 释放 "Simple.mainThread" 锁。

此时主线程将尝试在 "mainThread" 上再次获取锁,然后从调用 wait 的地方恢复。由于此时 "mainThread" 锁尚未获取,主线程获取锁并打印 "main woke up"。

现在,这里遇到了for循环。 记住: 这里锁定 "mainThread" class 对象已经被获取。

现在在 for 循环中,它获取 "printer" 对象上的锁。进行一些计算并调用 "printer.notifyAll()" 并通知所有等待 "printer" 对象的线程。

** 此处要记住的是:由于代码游标仍在 "synchronized (printer)" 内,因此尚未释放 "printer" 对象上的锁。 **

向前移动,打印 "main sleeping",然后调用 "mainThread.wait()"。这会尝试获取已获取的 "mainThread" 上的锁(上面提到 "Remember :" 在块中)并卡住,因为此后没有线程通知 "mainThread" 并且 "synchronized (printer)" 块永远不会结束即即使在调用 NotifyAll() 之后也不会释放对 "printer" 对象的锁定。

尝试在开始的 main 方法中添加以下代码,以测试上面的场景。

synchronized (mainThread) {
            synchronized (printer){
                System.out.println("Before");
                mainThread.wait();
                System.out.println("After");
            }

解决方案: 在 "printer.notifyAll()" 之后关闭 "synchronized (printer)" 块,以便在通知之后和获取 "mainThread".

之前释放 "printer" 锁