繁体   English   中英

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

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

我有一个简单的多线程算法,用于在后台线程中加载一系列文件,并在加载完成后让JPanel显示第一个图像。 在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);
}

我的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) {
        }
    }
}

我已经验证执行在调用线程唤醒之前多次(通常> 20)通过notifyAll()并在帧中显示图像。 我还验证了images对象实际上是与等待的对象相同的对象。 我尝试添加yield() ,但这没有帮助。 为什么调用notifyAll()不会立即唤醒等待线程?

尝试这个..

1. wait()立即释放锁定。

2. notify() or notifyAll() 不会立即释放锁,但会一直到达同步块的结束括号。

3.出于上述目的,在java.util.concurrent package使用CountDownLatch

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

您的加载程序线程正在循环,可能会在放弃其时间片之前立即重新caller.images上的监视器。

等待线程必须重新获取监视器才能取得进展 - 由于加载程序再次抓取它,它无法做到。

你在这里尝试实现的目标并不是很清楚,但是开始一个新线程然后在构造函数中等待通常是一个非常糟糕的主意。 哎呀,如果在第一张图片加载之前你根本无法做任何事情,为什么不同步呢? 无论如何,这就是你有效的做法......

“简单的多线程算法”是矛盾的。 线程总是很难。 使用来自java.util.concurrent的抽象(它们也很难,但有些人认为它们可以管理),除非你的名字是Brian Goetz。

代码中的两个大错误是:

  • 首先,有可能加载程序在Swing执行之前获取锁定,并且在结束之前它不会回复它(是的,它会偶尔放弃它,但它有可能会抓住它再次)

  • 因为您的JPanel构造函数是在EDT中调用的(如果不是,那么您会有更大的错误),paintComponent将不会被调用,直到构造函数完成(只有一件事可以在Swing中同时发生)。 所以无论如何你都无法实现你想要的。

还有更多,但如果你修复上面的两个,其余的将是无关紧要的。

我建议你删除整个等待的东西,并:

  • 使列表同步(使用images = Collections.synchronizedList(new ArrayList()););
  • 在加载器中添加一个调用caller.repaint(),而不是images.notifyAll()

更新:我刚读了一条评论,你的意图是在第一张图片加载之前实际停止EDT。 在这种情况下,你的整个目标是错误的:阻止EDT是一个重大错误。

暂无
暂无

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

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