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?
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…