2

一連のファイルをバックグラウンドスレッドにロードするように設計された単純なマルチスレッドアルゴリズムがあり、ロードが完了するとすぐに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.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) {
        }
    }
}

notifyAll()呼び出し元のスレッドがウェイクアップしてフレームに画像を表示する前に、実行が数回(通常は> 20)通過することを確認しました。また、imagesオブジェクトが実際に待機しているオブジェクトと同じオブジェクトであることを確認しました。を追加しようとしましたyield()が、役に立ちませんでした。notifyAll()待機中のスレッドをすぐに起こさないようにするための呼び出しはなぜですか?

4

3 に答える 3

5

これを試して..

1. すぐwait()に、ロックを解除します。

2. すぐにロックを解除することはありませんが、同期されたブロックの終了中括弧に到達するまでロックを解除しますnotify() or notifyAll()

3.上記の目的のために、で使用CountDownLatchします。java.util.concurrent package

于 2012-07-09T19:02:18.280 に答える
4

呼び出し元のスレッドがウェイクアップしてフレームに画像を表示する前に、実行がnotifyAll()を数回(通常は> 20)通過することを確認しました。

ローダースレッドがループしていて、おそらくタイムスライスを放棄する前に、すぐにモニターを再取得しています。caller.images

待機中のスレッドは、進行する前にモニターを再取得する必要があります。これは、ローダーがモニターを再度取得したために実行できません。

ここで何を達成しようとしているのかは明確ではありませんが、新しいスレッドを開始してからコンストラクター内で待機することは、一般的に非常に悪い考えです。最初の画像が読み込まれるまで何もできない場合は、同期して実行してみませんか?それはとにかくあなたが効果的にやっていることです...

于 2012-07-09T19:00:40.090 に答える
4

「単純なマルチスレッドアルゴリズム」は撞着語です。スレッドは常にハードです。あなたの名前がBrianGoetzでない限り、java.util.concurrentからの抽象化を使用してください(それらも難しいですが、いくつかの考えでは管理可能である可能性があります)。

コードの2つの大きなエラーは次のとおりです。

  • まず第一に、ローダーがSwingが行う前にロックを保持し、それが終了するまでロックを戻さない可能性があります(はい、たまにそれをあきらめますが、それをつかむ可能性がありますまた)

  • JPanelコンストラクターはEDTで呼び出されるため(そうでない場合は、さらに大きなエラーが発生します)、コンストラクターが終了するまでpaintComponentは呼び出されません(Swingで一度に発生する可能性があるのは1つだけです)。したがって、とにかくあなたが望むことを達成することはできません。

もっとありますが、上記の2つを修正すると、残りは無関係になります。

待機しているものをすべて削除して、次のことを行うことをお勧めします。

  • リストを同期します(images = Collections.synchronizedList(new ArrayList());を使用します);
  • images.notifyAll()の代わりに、呼び出し側caller.repaint()をローダーに追加します

更新:最初の画像が読み込まれるまで実際にEDTを停止することを意図しているというコメントの1つを読みました。そのような場合、あなたの全体の目的は間違っています:EDTをブロックすることは大きな間違いです。

于 2012-07-09T19:11:30.553 に答える