web-dev-qa-db-ja.com

Javaスレッドの終了を待つ

データをダウンロードするスレッドがあり、ダウンロードが完了するまで待ってからデータをロードします。これを行う標準的な方法はありますか?

詳細:

URL(シリアル化されたPOJO)からデータを取得するダウンロードクラスがあります。ダウンロードは実行可能および監視可能です。ダウンロードされたバイトとダウンロードサイズを追跡します。ユーザーに進行状況を表示する進行状況バーがあります。 GUIは、ダウンロードを監視して進行状況バーを更新します。

POJOがダウンロードされたら、それを入手して次のステップに進みます。各ステップは、前のステップが完了するまで待機する必要があります。問題は、ダウンロードスレッドを待つためにアプリケーションを一時停止する方法が考えられないことです。ダウンロードが完了したら、download.getObject()を呼び出して、データをオブジェクトとして返します。その後、キャストして次のダウンロードに進むことができます。

ダウンロード用のURLを管理し、Downloadへのすべての呼び出しを行うヘルパークラスがあります。この呼び出しはgetObjectを呼び出してキャストを実行します。 Guiはhelper.getUser()を呼び出します。ヘルパーはスレッドの実行を開始し、キャストされたオブジェクトを返すことができるように、スレッドが終了したときに「認識」してもらいたい。

提案/例はありますか?私はこの設計の初期段階にいるので、変更したいと思っています。

よろしくお願いします。

更新:

http://download.Oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get に従い、モーダルを使用してスレッドが終了するまでブロックしました。コードは非常に乱雑だったので、このアプローチは好きではありません。ダウンロードプロセスのワークフローを処理する「クリーンな」方法を探し続けます。

53
Allan

Threadには、それを行うメソッドがあります- join スレッドの実行が完了するまでブロックします。

68
unholysampler

Java.util.concurrentパッケージのCountDownLatchを使用できます。 1つ以上のスレッドが完了するのを待ってから、待機中のスレッドで実行を継続する場合に非常に便利です。

たとえば、次の3つのタスクが完了するまで待機します。

CountDownLatch latch = new CountDownLatch(3);
...
latch.await(); // Wait for countdown

他のスレッドは、タスクが完了するとそれぞれlatch.countDown()を呼び出します。カウントダウンが完了すると、この例では3つ、実行が継続されます。

51
permagnusson

SwingWorker には、タスクを実行するために使用できるdoInBackground()があります。 get()を呼び出してダウンロードが完了するのを待つか、SwingWorkerが完了するとイベントディスパッチスレッドで呼び出されるdone()メソッドをオーバーライドできます。

Swingworkerには、探している機能の多くが備わっているため、車輪を再発明する必要がないという点で、現在のアプローチよりも優れています。 getProgress()メソッドとsetProgress()メソッドを、ダウンロードの進行状況の実行可能オブジェクトのオブザーバーの代替として使用できます。前述のdone()メソッドは、ワーカーの実行が完了してEDTで実行された後に呼び出されます。これにより、ダウンロードの完了後にデータをロードできます。

13
tstevens

join() メソッドのより良い代替手段は、一定期間にわたって進化してきました。

ExecutorService.html#invokeAll は1つの選択肢です。

指定されたタスクを実行し、すべてが完了したときにステータスと結果を保持するFutureのリストを返します。 Future.isDone()は、返されたリストの各要素に対してtrueです。

完了したタスクは、正常に終了するか、例外をスローすることで終了する可能性があることに注意してください。この操作の進行中に特定のコレクションが変更された場合、このメソッドの結果は未定義です。

ForkJoinPool または Executors.html#newWorkStealingPool は、同じ目的を達成するための他の代替手段を提供します。

コードスニペットの例:

import Java.util.concurrent.*;

import Java.util.*;

public class InvokeAllDemo{
    public InvokeAllDemo(){
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        List<MyCallable> futureList = new ArrayList<MyCallable>();
        for ( int i=0; i<10; i++){
            MyCallable myCallable = new MyCallable((long)i);
            futureList.add(myCallable);
        }
        System.out.println("Start");
        try{
            List<Future<Long>> futures = service.invokeAll(futureList);  
        }catch(Exception err){
            err.printStackTrace();
        }
        System.out.println("Completed");
        service.shutdown();
    }
    public static void main(String args[]){
        InvokeAllDemo demo = new InvokeAllDemo();
    }
    class MyCallable implements Callable<Long>{
        Long id = 0L;
        public MyCallable(Long val){
            this.id = val;
        }
        public Long call(){
            // Add your business logic
            return id;
        }
    }
}
8
Ravindra babu

join()を使用して、すべてのスレッドが終了するのを待つことができます。スレッドの作成時に、スレッドのすべてのオブジェクトをグローバルArrayListに保持します。その後、以下のようにループを維持します。

for (int i = 0; i < 10; i++) 
{
    Thread T1 = new Thread(new ThreadTest(i));                
    T1.start();                
    arrThreads.add(T1);
} 

for (int i = 0; i < arrThreads.size(); i++) 
{
    arrThreads.get(i).join(); 
}

詳細についてはこちらをご覧ください: http://www.letmeknows.com/2017/04/24/wait-for-threads-to-finish-Java

5
user7923798

SwingWorkerが提供するようなバックグラウンドスレッドでダウンロードを呼び出していると思います。その場合、同じSwingWorkerのdoInBackgroundメソッドで次のコードを順番に呼び出します。

一般的に、スレッドが終了するのを待ちたいときは、 join() を呼び出す必要があります。

3
biziclop

提案/例はありますか? SwingWorker ...に従いました。コードは非常に乱雑で、このアプローチは好きではありません。

完了を待つget()の代わりに、process()およびsetProgress()を使用して、この単純な または関連する

1
trashgod

join() メソッドを使用すると、1つのスレッドが別のスレッドの完了を待つことができますが、スリープと同様に、結合はタイミングについてOSに依存するため、結合が正確に長く待機すると想定しないでください指定どおり。

0
Jatin Mandanka