简体   繁体   中英

Java wait/notify - not waking up thread

I'm trying to do a small exercise to get used to wait/notify. What i'm trying to do is simply start a thread, then put it to sleep with wait and wake it up with notify, multiple times.

My code is:

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

}

I would expect this to go something like

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
...

Instead this does:

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

So... it seems like notify isn't waking up the printer thread?

It shouldn't be a deadlock since by waiting i realease all the locks i have, so the main shoudn't have any lock over printer and printer should be able to wake up and print.

What am i doing wrong?

Most likely your notifyAll() call is being called before Print calls wait() again. The problem is your reliance on the wait and notifyAll calls happening in exactly the sequence you'd like them to. These are two different threads of execution so of course this is not guaranteed, and thus you are getting what you've got.

A MUCH better way to accomplish this would be to create a common 3rd shared object that both threads could acquire lock on. This would synchronize both threads as they wait to get access to this object.

Also, you should read the Javadocs for Thread.wait, notify, and notifyAll. If/when you do, you will see that you should NEVER call these methods on Threads, as they are used in doing thread.join (Not only that, but my "claim to fame" is I believe it was my bug request years ago to document this when this wasn't in the JavaDoc that resulted in it being added to the Javadoc. Could have been someone else, but it happened right after I asked for it:))

Property: calling wait() releases the lock (on which it was monitoring) and goes to waiting state. It waits for notify() or notifyAll() on same object. Once notify() or notifyAll() and it gets scheduled in CPU, it again acquires the lock before resuming.

When you did first "synchronized (mainThread)" in main method, it basically took lock on "mainThread" class object. When mainThread.wait() is called, mainThread went in waiting state (waiting for someone to call notify or notifyAll on mainThread class object).

By this time PrintThread might get the CPU. This is when "synchronized (Simple.mainThread)" gets scheduled and takes lock on "Simple.mainThread" and notifies all the Threads waiting on "Simple.mainThread". Here just after this block is complete, PrintThread releases the "Simple.mainThread" lock.

At this point main Thread will try acquiring lock on "mainThread" again before resuming from where wait was called. Since by this point "mainThread" lock is not acquired, main thread gets the lock and prints "main woke up".

Now, for loop is encountered here. Remember: here that lock on "mainThread" class Object is already acquired.

Now Inside for loop, it acquires lock on "printer" object. Does some computation and "printer.notifyAll()" is called and all threads waiting on "printer" object will be notified.

**Point to remember here is: Since code cursor is still inside "synchronized (printer)", lock on "printer" object is not yet released. **

Moving forward, "main sleeping" is printed and after that "mainThread.wait()" is called. This tries to acquire lock on "mainThread" which is already acquired (mentioned above where "Remember:" in block) and gets stuck as no thread is notifying on "mainThread" hereafter and "synchronized (printer)" block never ends ie lock on "printer" object is never released even after NotifyAll() is called.

try adding below code in main method in the start, to test above scenario.

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

SOLUTION: Close "synchronized (printer)" block just after "printer.notifyAll()", so that "printer" lock is released after notifying and before acquiring "mainThread".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM