简体   繁体   English

为什么我的notify()没有唤醒等待线程?

[英]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. 我有一个简单的多线程算法,用于在后台线程中加载一系列文件,并在加载完成后让JPanel显示第一个图像。 In the constructor of the JPanel, I start the loader, then wait on the list of images, as follows: 在JPanel的构造函数中,我启动加载器,然后在图像列表上等待,如下所示:

//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线程看起来像这样:

//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. 我已经验证执行在调用线程唤醒之前多次(通常> 20)通过notifyAll()并在帧中显示图像。 I have also verified that the images object is in fact the same object as the one being waited on. 我还验证了images对象实际上是与等待的对象相同的对象。 I have attempted adding a yield() , but that did not help. 我尝试添加yield() ,但这没有帮助。 why does the call to notifyAll() not wake the waiting thread immediately? 为什么调用notifyAll()不会立即唤醒等待线程?

Try this.. 尝试这个..

1. wait() will immediately , release the lock. 1. wait()立即释放锁定。

2. notify() or notifyAll() will not immediately release the lock, but will have it till the ending braces of the synchronized block is reached.. 2. notify() or notifyAll() 不会立即释放锁,但会一直到达同步块的结束括号。

3. Use CountDownLatch in java.util.concurrent package , for your above mentioned purpose. 3.出于上述目的,在java.util.concurrent package使用CountDownLatch

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. 我已经验证执行在调用线程唤醒之前多次(通常> 20)通过notifyAll()并在帧中显示图像。

Your loader thread is looping, immediately reacquiring the monitor on caller.images , probably before it gives up its timeslice. 您的加载程序线程正在循环,可能会在放弃其时间片之前立即重新caller.images上的监视器。

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. 使用来自java.util.concurrent的抽象(它们也很难,但有些人认为它们可以管理),除非你的名字是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) 首先,有可能加载程序在Swing执行之前获取锁定,并且在结束之前它不会回复它(是的,它会偶尔放弃它,但它有可能会抓住它再次)

  • 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). 因为您的JPanel构造函数是在EDT中调用的(如果不是,那么您会有更大的错误),paintComponent将不会被调用,直到构造函数完成(只有一件事可以在Swing中同时发生)。 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()); ); 使列表同步(使用images = Collections.synchronizedList(new ArrayList()););
  • add a call caller.repaint() in the loader, instead of the images.notifyAll() 在加载器中添加一个调用caller.repaint(),而不是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. 更新:我刚读了一条评论,你的意图是在第一张图片加载之前实际停止EDT。 In such case, your whole aim is wrong: blocking EDT is a major mistake. 在这种情况下,你的整个目标是错误的:阻止EDT是一个重大错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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