【问题标题】:Java wait/notify - not waking up threadJava 等待/通知 - 不唤醒线程
【发布时间】:2020-08-31 06:33:48
【问题描述】:

我正在尝试做一个小练习来习惯等待/通知。 我想要做的只是启动一个线程,然后让它进入休眠等待并多次通知唤醒它。

我的代码是:

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

所以...好像 notify 没有唤醒打印机线程?

这不应该是一个死锁,因为通过等待我释放了我拥有的所有锁,所以 ma​​in 不应该对 printer 有任何锁定打印机应该能够唤醒并打印。

我做错了什么?

【问题讨论】:

    标签: java multithreading wait notify


    【解决方案1】:

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

    当你在主方法中第一次“同步(mainThread)”时,它基本上锁定了“mainThread”类对象。当 mainThread.wait() 被调用时,mainThread 进入等待状态(等待某人调用 mainThread 类对象的 notify 或 notifyAll)。

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

    此时主线程将在从调用等待的位置恢复之前再次尝试获取“主线程”上的锁定。由于此时尚未获得“mainThread”锁,因此主线程获取锁并打印“main wake up”。

    现在,这里遇到了 for 循环。 记住:这里已经获得了“mainThread”类对象的锁定。

    现在在 for 循环中,它获取“打印机”对象的锁定。是否进行一些计算并调用“printer.notifyAll()”,并且将通知所有等待“printer”对象的线程。

    **这里要记住的一点是:由于代码光标仍在“同步(打印机)”内,因此尚未释放对“打印机”对象的锁定。 **

    向前移动,打印“main sleep”,然后调用“mainThread.wait()”。这试图获取已经获取的“mainThread”上的锁定(上面提到了“记住:”在块中的位置)并被卡住,因为此后没有线程通知“mainThread”并且“同步(打印机)”块永远不会结束,即锁定即使在 NotifyAll() 被调用后,"printer" 对象也不会被释放。

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

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

    解决方案: 在“printer.notifyAll()”之后关闭“synchronized (printer)”块,以便在通知之后和获取“mainThread”之前释放“printer”锁。

    【讨论】:

      【解决方案2】:

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

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

      此外,您应该阅读 Thread.wait、notify 和 notifyAll 的 Javadocs。如果/当你这样做时,你会看到你不应该在线程上调用这些方法,因为它们用于执行 thread.join (不仅如此,但我的“声名鹊起”是我相信这是我多年前的错误请求当它不在导致它被添加到 Javadoc 的 JavaDoc 中时记录这一点。可能是其他人,但它发生在我要求它之后:))

      【讨论】:

      • Mhh...这确实有道理,如果主调用在打印机进入睡眠状态之前通知,那么整个程序将被阻止。但是......输出说等待首先被调用......那么等待开始执行但在通知开始之前没有结束执行有可能吗?
      • @FedericoCapece 正如我所说,你这样做是错误的,你永远不会让它以你尝试的方式工作
      • 刚刚尝试在单个对象上同步而不是在两个不同的线程上同步,然后......它成功了,非常感谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-05
      • 1970-01-01
      相关资源
      最近更新 更多