4

多くの URL を処理する必要がある Java プログラムを作成しています。
各 URL は次のジョブを順番に実行します: ダウンロード、分析、圧縮

URL ごとに 1 つのスレッドですべてのジョブを一度に実行するのではなく、各ジョブに一定量のスレッドを持たせて、すべてのジョブで同時にスレッドが常に実行されるようにします。

たとえば、ダウンロード ジョブには、URL を取得してダウンロードするための複数のスレッドがあり、URL の 1 つがダウンロードされるとすぐに分析ジョブのスレッドに渡され、完了するとすぐに次のスレッドに渡されます。圧縮ジョブのスレッドなど

終了するとすぐに結果を返すため、JavaでCompletionServiceを使用することを考えていますが、それがどのように機能するかわかりません。これまでのところ、私のコードは次のようになります。

ExecutorService executor = Executors.newFixedThreadPool(3);
CompletionService<DownloadedItem> completionService = new ExecutorCompletionService<DownloadedItem>(executor);

//while list has URL do {
   executor.submit(new DownloadJob(list.getNextURL());//submit to queue for download
//}

//while there is URL left do {
   Future<DownloadedItem> downloadedItem = executor.take();//take the result as soon as it finish
   //what to do here??
//}

私の質問は、ダウンロードしたアイテムを分析ジョブに移動し、すべてのダウンロード ジョブが完了するのを待たずにそこで作業を行うにはどうすればよいですか? ジョブごとに CompletionService を作成することを考えていますが、それは実行可能な方法ですか? そうでない場合、この問題を解決するためのより良い代替方法はありますか? 例を挙げてください。

4

3 に答える 3

3

順番IN ORDERにタスクに別のスレッドを使用しようとすると、システムの設計が複雑になるだけです。

私の意見では、個別のスレッドで個別の URL を一度に処理するのが最善の方法です。3 つのステップを実行するには、別の抽象化 (3 つの callable を使用するなど) を導入できますが、1 つのスレッドでそれらを順次実行する必要があります。また、完了サービスは必要ありません。

于 2012-09-24T21:00:56.923 に答える
1

あなたが説明しているものはPipelineと呼ばれます。基本的に、ダウンロード タスクの出力は分析タスクの入力です。analyze の出力は、compress の入力です。これを実現するには、次の 2 つのオプションがあるようです。

1) ダウンロード タスクに出力用のパイプラインを知らせて、結果自体を送信できるようにします。

class DownloadTask implement Runnable {
    Executor analyzePipeline;
    public void run() {
        //Do download stuff
        analyzePipeline.submit(new AnalyzeTask(downloaded content));
    }
}

2) 別のスレッドが結果をダウンロード タスクから分析タスクのパイプラインに移動できるようにします。

ExecutorService executor = Executors.newFixedThreadPool(3);
ExecutorService analyzeExecutor = Executors.newFixedThreadPool(3);
CompletionService<DownloadedItem> completionService = new ExecutorCompletionService<DownloadedItem>(executor);

while list has URL do {
   executor.submit(new DownloadJob(list.getNextURL());//submit to queue for download
}

new Thread() {
    public void run() {
        while there is URL left do {
            Future<DownloadedItem> downloadedItem = executor.take();//take the result as soon as it finish
            analyzeExecutor.submit(new AnalyzeJob(downloadedItem.get());
        }
    }
};    
//...and so on
于 2012-09-24T21:22:04.733 に答える
1

あなたはかなり近いです。CompletionService最初に代わりにタスクを送信します。

completionService.submit(new DownloadJob(list.getNextURL());

今すぐつかんFutureでそれを待ちます:

DownloadedItem> downloadedItem = executor.take().get();

への呼び出しget()がブロックされる可能性があります。送信したアイテムの数だけ上記の行を繰り返します。


はるかに高いスループットが必要な場合 (この場合、一度に最大 3 つの URL がダウンロードされます)、async-http-client文字通り何千もの URL から同時にダウンロードできる方法を検討してください。NIO を使用し、イベント ドリブンであり、スレッド化は必要ありません。

于 2012-09-24T20:59:53.380 に答える