我有一个接受查询数组的方法,我需要针对不同的搜索引擎 Web API(例如 Google 或 Yahoo)运行它们。为了并行化进程,为每个查询生成一个线程,然后是 join
。 ed 最后,因为我的应用程序只能在之后 继续,所以我有每个 查询的结果。我目前有一些类似的东西:
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
接口(interface)、FutureTask
和 ExecutorService
。我想知道这个新 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 的新手,我想知道上面的代码是否有可以改进的地方,是否比第一个选项(使用 线程
)。有些类我没有探索,例如 FutureTask
等。我也很想听听有关这方面的任何建议。
最佳答案
您的代码有几个问题。
- 您可能应该使用 ExecutorService.invokeAll() 方法。 创建新线程和新线程池的成本可能很高(尽管可能无法与调用外部搜索引擎相比)。 invokeAll() 可以为您管理线程。
- 您可能不想混合使用数组和泛型。
- 您正在调用 aggregatedResults.add() 而不是 addAll()。
- 当成员变量可以是 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;
}
}
关于java - 多线程搜索操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1150073/