マルチスレッド検索操作
-
18-09-2019 - |
質問
クエリの配列を受け取るメソッドがあり、それらを 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;
}
}
最近、あることに気づきました。 新しい 同時ジョブを実行するための Java の API。つまり、 Callable
インターフェース、 FutureTask
そして ExecutorService
. 。この新しい API が使用すべきものなのか、従来の API よりも効率的なのかどうか疑問に思っていましたが、 Runnable
そして Thread
.
この新しい 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
, 、など。それについてのアドバイスもいただければ幸いです。
解決
コードにいくつかの問題があります。
- おそらく ExecutorService.invokeAll() メソッドを使用する必要があります。新しいスレッドと新しいスレッド プールを作成するコストは膨大になる可能性があります (ただし、外部検索エンジンを呼び出すことに比べればそうではないかもしれません)。invokeAll() はスレッドを管理できます。
- おそらく、配列とジェネリックを混合したくないでしょう。
- addAll() の代わりに、aggregatedResults.add() を呼び出しています。
- メンバー変数が 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;
}
}
他のヒント
さらなる改善として、 完了サービスこれにより、送信と取得の順序が切り離され、代わりに将来のすべての結果がキューに配置され、完了した順序で結果が取得されます。
使用を提案できますか Future.get() とタイムアウト ?
それ以外の場合は、1 つの検索エンジンが応答しなくなるだけですべてが停止します (たとえば、端末にネットワークの問題がある場合は、検索エンジンの問題である必要さえありません)。