データをダウンロードするスレッドがあり、ダウンロードが完了するまで待ってからデータをロードします。これを行う標準的な方法はありますか?
詳細:
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 に従い、モーダルを使用してスレッドが終了するまでブロックしました。コードは非常に乱雑だったので、このアプローチは好きではありません。ダウンロードプロセスのワークフローを処理する「クリーンな」方法を探し続けます。
Thread
には、それを行うメソッドがあります- join スレッドの実行が完了するまでブロックします。
Java.util.concurrent
パッケージのCountDownLatch
を使用できます。 1つ以上のスレッドが完了するのを待ってから、待機中のスレッドで実行を継続する場合に非常に便利です。
たとえば、次の3つのタスクが完了するまで待機します。
CountDownLatch latch = new CountDownLatch(3);
...
latch.await(); // Wait for countdown
他のスレッドは、タスクが完了するとそれぞれlatch.countDown()
を呼び出します。カウントダウンが完了すると、この例では3つ、実行が継続されます。
SwingWorker には、タスクを実行するために使用できるdoInBackground()
があります。 get()
を呼び出してダウンロードが完了するのを待つか、SwingWorkerが完了するとイベントディスパッチスレッドで呼び出されるdone()
メソッドをオーバーライドできます。
Swingworkerには、探している機能の多くが備わっているため、車輪を再発明する必要がないという点で、現在のアプローチよりも優れています。 getProgress()
メソッドとsetProgress()
メソッドを、ダウンロードの進行状況の実行可能オブジェクトのオブザーバーの代替として使用できます。前述のdone()
メソッドは、ワーカーの実行が完了してEDTで実行された後に呼び出されます。これにより、ダウンロードの完了後にデータをロードできます。
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;
}
}
}
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
SwingWorkerが提供するようなバックグラウンドスレッドでダウンロードを呼び出していると思います。その場合、同じSwingWorkerのdoInBackgroundメソッドで次のコードを順番に呼び出します。
一般的に、スレッドが終了するのを待ちたいときは、 join() を呼び出す必要があります。
提案/例はありますか?
SwingWorker
...に従いました。コードは非常に乱雑で、このアプローチは好きではありません。
完了を待つget()
の代わりに、process()
およびsetProgress()
を使用して、この単純な 例 または関連する 例 。
join() メソッドを使用すると、1つのスレッドが別のスレッドの完了を待つことができますが、スリープと同様に、結合はタイミングについてOSに依存するため、結合が正確に長く待機すると想定しないでください指定どおり。