简体   繁体   中英

why does my notify() not wake a waiting thread?

I have a simple multithreaded algorithm designed to load a series of files in a background thread, and have a JPanel display the first image as soon as it is done loading. In the constructor of the JPanel, I start the loader, then wait on the list of images, as follows:

//MyPanel.java
public ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
int frame;    

public MyPanel(String dir){
    Thread loader = new thread (new Loader(dir, this));
    loader.start();

    frame = 0;
    //miscellaneous stuff

    synchronized(images){
        while (images.isEmpty()){
            images.wait();
        }
    }

    this.setPrefferedSize(new Dimension(800,800));
}

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g)

    g.drawImage(images.get(frame), 0, 0, 800, 800, null);
}

my Loader thread looks like this:

//Loader.java
String dir;
MyPanel caller;

public Loader(String dir, MyPanel caller){
    this.dir = dir;
    this.caller = caller;
}

@Override
public void run(){
    File dirFile = new File(dir);
    File[] files = dirFile.listFiles();
    Arrays.sort(files);

    for (File f : files) {
        try {
            synchronized (caller.images) {
                BufferedImage buffImage = ImageIO.read(f);
                caller.images.add(buffImage);
                caller.images.notifyAll();
            }
        } catch (IOException e) {
        }
    }
}

I have verified that execution passes through notifyAll() several times (usually >20) before the calling thread wakes up and displays the image in the frame. I have also verified that the images object is in fact the same object as the one being waited on. I have attempted adding a yield() , but that did not help. why does the call to notifyAll() not wake the waiting thread immediately?

Try this..

1. wait() will immediately , release the lock.

2. notify() or notifyAll() will not immediately release the lock, but will have it till the ending braces of the synchronized block is reached..

3. Use CountDownLatch in java.util.concurrent package , for your above mentioned purpose.

I have verified that execution passes through notifyAll() several times (usually >20) before the calling thread wakes up and displays the image in the frame.

Your loader thread is looping, immediately reacquiring the monitor on caller.images , probably before it gives up its timeslice.

The waiting thread has to reacquire the monitor before it can make progress - which it can't do because the loader has grabbed it again.

It's not really clear what you're trying to achieve here, but starting a new thread and then waiting within a constructor is generally a really bad idea. Heck, if you can't do anything at all until the first image has loaded, why not do that synchronously? That's what you're effectively doing anyway...

"a simple multithreaded algorithm" is an oxymoron. Threads are always hard. Use abstractions from java.util.concurrent (they are also hard, but with some thought they can be managable) unless your name is Brian Goetz.

The two big errors in your code are:

  • first of all, it is possible that loaders gets hold of the lock before Swing does and that it will not give it back until it is finished (yes, it gives it up once in a while, but it is possible that it will grab it again)

  • since your JPanel constructor is called in EDT (and if it is not, then you have an even bigger error), paintComponent WILL NOT BE CALLED UNTILL THE CONSTRUCTOR IS FINISHED (only one thing can be happening in Swing at once). So you will not achieve what you want anyway.

There is more, but if you fix the two above, the rest will be irrelevant.

I suggest that you delete the whole waiting thing and:

  • make the list synchronized (using images = Collections.synchronizedList(new ArrayList()); );
  • add a call caller.repaint() in the loader, instead of the images.notifyAll()

update: I just read in one of the comments that your intention was to actually stop the EDT until the first image is loaded. In such case, your whole aim is wrong: blocking EDT is a major mistake.

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