有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程Java等待/通知未唤醒线程

我正在做一个小练习来适应等待/通知。 我试图做的是简单地启动一个线程,然后用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

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

这不应该是一个死锁,因为通过等待,我可以解除所有的锁定,所以不应该对打印机有任何锁定,打印机应该能够唤醒并打印

我做错了什么


共 (2) 个答案

  1. # 1 楼答案

    很可能是在再次调用Print calls wait()之前调用了notifyAll()调用。问题在于你依赖于等待和通知,所有的呼叫都是按照你希望的顺序进行的。这是两个不同的执行线程,所以当然这不是保证,因此你得到了你所得到的

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

    此外,您还应该阅读Java文档中的线程。等等,通知,然后通知所有人。如果/当您这样做时,您将看到您永远不应该在线程上调用这些方法,因为它们用于执行线程。join(不仅如此,我的“声名远扬”是因为我相信几年前我的错误请求记录了这一点,而这并不是在JavaDoc中,这导致它被添加到JavaDoc中。可能是其他人,但它发生在我请求之后:)

  2. # 2 楼答案

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

    当您在main方法中第一次执行“synchronized(mainThread)”时,它基本上锁定了“mainThread”类对象。当主线程。调用wait(),mainThread进入等待状态(等待有人在mainThread类对象上调用notify或notifyAll)

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

    此时,主线程将再次尝试获取“主线程”上的锁,然后从调用wait的位置恢复。由于此时未获取“mainThread”锁,因此主线程获取锁并打印“main wakeup”

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

    现在在for循环内部,它获得了对“printer”对象的锁定。执行一些计算,并调用“printer.notifyAll()”,所有等待“printer”对象的线程都将收到通知

    **这里需要记住的一点是:由于代码光标仍在“synchronized(printer)”内,“printer”对象的锁定尚未解除。**

    向前看,会打印“main sleeping”,然后调用“mainThread.wait()”。这将尝试获取已获取的“mainThread”上的锁(如上所述,其中“记住:”在块中),并被卡住,因为此后没有线程在“mainThread”上进行通知,“synchronized(printer)”块永远不会结束,即即使在调用NotifyAll()之后,“printer”对象上的锁也永远不会解除

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

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

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