3

クエリの配列を受け取るメソッドがあり、それらを Google や Yahoo などのさまざまな検索エンジン Web API に対して実行する必要があります。プロセスを並列化するために、クエリごとにスレッドが生成され、最後にスレッドが生成されます。これは、すべてのクエリの結果が得られた後joinにのみアプリケーションを続行できるためです。私は現在、これらの行に沿って何かを持っています:

public abstract class class Query extends Thread {
    private String query;

    public abstract Result[] querySearchEngine();
    @Override
    public void run() {
        Result[] results = querySearchEngine(query);
        Querier.addResults(results);
    }

}

public class GoogleQuery extends Query {
    public Result querySearchEngine(String query) { 
        // access google rest API
    }
}

public class Querier {
    /* Every class that implements Query fills this array */
    private static ArrayList<Result> aggregatedResults;

    public static void addResults(Result[]) { // add to aggregatedResults }

    public static Result[] queryAll(Query[] queries) {
        /* for each thread, start it, to aggregate results */
        for (Query query : queries) {
            query.start();
        }
        for (Query query : queries) {
            query.join();
        }
        return aggregatedResults;
    }
}

最近、並行ジョブを実行するための新しいAPI が Java にあることを発見しました。つまり、Callableインターフェイス、FutureTaskおよびExecutorService. この新しい API を使用する必要があるかどうか、また従来の API よりも効率的かどうかを考えていましRunnableThread。.

この新しい API を調べた後、次のコード (簡易版) を思いつきました。

   public abstract class Query implements Callable<Result[]> {
        private final String query; // gets set in the constructor

        public abstract Result[] querySearchEngine();
        @Override
        public Result[] call() {
            return querySearchEngine(query);
        }
    }

public class Querier {   
        private ArrayList<Result> aggregatedResults;

        public Result[] queryAll(Query[] queries) {
            List<Future<Result[]>> futures = new ArrayList<Future<Result[]>>(queries.length);
            final ExecutorService service = Executors.newFixedThreadPool(queries.length);  
            for (Query query : queries) {
                futures.add(service.submit(query));  
            }
            for (Future<Result[]> future : futures) {  
                aggregatedResults.add(future.get());  // get() is somewhat similar to join?
            }  
            return aggregatedResults;
        }
    }

私はこの同時実行 API を初めて使用します。上記のコードで改善できる点があるかどうか、および最初のオプション (を使用) よりも優れているかどうかを知りたいThreadです。など、私が調べていないクラスがいくつかありますFutureTask。その辺のアドバイスも頂ければ幸いです。

4

3 に答える 3

7

コードにいくつかの問題があります。

  1. おそらく ExecutorService.invokeAll() メソッドを使用する必要があります。新しいスレッドと新しいスレッド プールを作成するコストは、かなりの額になる可能性があります (ただし、外部の検索エンジンを呼び出す場合とは比べ物にならないかもしれません)。invokeAll() はスレッドを管理できます。
  2. おそらく、配列とジェネリックを混在させたくないでしょう。
  3. addAll() の代わりに aggregatedResults.add() を呼び出しています。
  4. メンバー変数が queryAll() 関数呼び出しに対してローカルである可能性がある場合は、メンバー変数を使用する必要はありません。

したがって、次のようなものが機能するはずです。

public abstract class Query implements Callable<List<Result>> {
    private final String query; // gets set in the constructor

    public abstract List<Result> querySearchEngine();
    @Override
    public List<Result> call() {
        return querySearchEngine(query);
    }
}

public class Querier {   
    private static final ExecutorService executor = Executors.newCachedThreadPool();

    public List<Result> queryAll(List<Query> queries) {
        List<Future<List<Result>>> futures = executor.submitAll(queries);
        List<Result> aggregatedResults = new ArrayList<Result>();
        for (Future<List<Result>> future : futures) {  
            aggregatedResults.addAll(future.get());  // get() is somewhat similar to join?
        }  
        return aggregatedResults;
    }
}
于 2009-07-19T15:17:38.463 に答える
4

さらなる改善として、CompletionService の使用を検討することができます。これ は、送信と取得の順序を分離し、代わりに、将来のすべての結果を、完了した順序で結果を取得するキューに配置します。

于 2009-07-19T15:10:58.663 に答える
3

Future.get() を timeout で使用することをお勧めできますか?

それ以外の場合は、1 つの検索エンジンが応答しなくなっただけですべてが停止します (たとえば、ネットワークに問題がある場合でも、検索エンジンの問題である必要はありません)。

于 2009-07-19T16:23:38.533 に答える