简体   繁体   中英

Why can't I use notifyAll() to wake up a waiting thread?

I have a thread class having the following two methods:

public void run()
{
    boolean running=true;
    while(running)
    {
        sendImage();
    }
}

private synchronized void sendImage()
{
        if(!imageready.getFlag())
        { 
            try
            {
                wait();
                System.out.println("woke up!");
            }
            catch(Exception e)
            {
                System.out.println("Exception raises in ImgSender while waiting");
            }

        }
        else
        {
            //send image
        }
}

I also has a GUI part with a mouse click method to open the file chooser window to let user choose file. The code

        public void mouseClicked (MouseEvent event) 
        {
           chooser=new JFileChooser();

           int status=chooser.showOpenDialog(null);


           if (status == JFileChooser.APPROVE_OPTION)
           { 
               try
               {
                    prepareImage();
               }
               catch(Exception e)
               {
                   System.out.println("file not opened");
                }
            }
         }

   private synchronized void prepareImage() throws Exception
    {
           System.out.println("File chosen");
           imgFile=chooser.getSelectedFile();  
           image.setImg( ImageIO.read(imgFile) );
           imageready.setFlag(true);
           notifyAll();  
           System.out.println("noticed other threads");
    }

But I can't wake the first thread up using the notifyAll() so that it could send the image.

As @MadProgrammer mentioned, you are not synchronizing on the same lock. A lock is, well, not sure how else to describe it. It's a lock. To lock and wait in Java, you pick an object to represent the "lock".

If 2 threads synchronize on 2 different objects, it's like the first thread saying "I am pumping gas in pump #1, if you need to use it, get in line", then the second thread saying "Hey, let me know when you're done with #2" (when pump 2 is out of service).

If you want the second driver to wait for the first driver to finish pumping, then he needs to declare interest in pump #1, the same pump that is being held by someone else. Likewise, if you want 2 threads to successfully perform a wait and notify, they need to refer to the same object.

Why this may appear confusing in your situation is that you aren't specifying explicit objects to lock on. You declared your methods as synchronized , which implicitly uses the instance of the object the method is executing against as the lock resource.

public class LockTest implements Runnable {
    public void synchronized doNothing() {
        try {
            System.out.println("Starting lock...");
            Thread.sleep(5000);
            System.out.println("Releasing lock...");
        }
        catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) {
        LockTest test1 = new LockTest();
        LockTest test2 = new LockTest();
        new Thread(test1).start();
        new Thread(test2).start();
        // Notice from the output that the threads do not block each other.
    }
}

We created 2 instances of LockTest , so they do not block each other since they have completely independent locks.

public class LockTest implements Runnable {
    private Object thingToLockWith;

    public LockTest(Object thingToLockWith) {
        this.thingToLockWith = thingToLockWith;
    }

    public void doNothing() {
        synchronized (thingToLockWith) {
            try {
                System.out.println("Starting lock...");
                Thread.sleep(5000);
                System.out.println("Releasing lock...");
            }
            catch (InterruptedException e) {
            }
        }
    }

    public static void main(String[] args) {
        Object lock = new Object();

        LockTest test1 = new LockTest(lock);
        LockTest test2 = new LockTest(lock);
        new Thread(test1).start();
        new Thread(test2).start();
        // Notice from the output they block.
    }

See how the output differs. The second thread has to wait in line for the first thread.

不要在Swing应用程序中直接使用线程API,而要使用Swing工作程序,只需在需要执行后台任务(例如发送图像且不想阻止GUI线程)时创建一个实例即可

It appears you are waiting on a the thread object and the gui object is doing the notify.

So when you do notifyAll(); do not expect that waiting on the thread object to wake up, as that is a completely different lock.

You can just use a common lock.

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