2009-07-19 133 views
3

我有一个接受一系列查询的方法,我需要针对不同的搜索引擎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; 
    } 
} 

最近,我发现有一个 API在Java中做兼职。即,Callable接口,FutureTaskExecutorService。我想知道这个新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等等。我很乐意听到关于此的任何建议。

+0

看起来不错,我不确定我会改变你的第二个例子。 在你的第一个例子中,我将扩展Runnable而不是Thread,但这只是挑剔。 – 2009-07-19 15:12:19

+0

+1,这对我来说已经够用了。 – akarnokd 2009-07-19 15:37:20

回答

7

您的代码有几个问题。

  1. 您应该使用ExecutorService.invokeAll()方法。 创建新线程和新线程池的成本可能很高(尽管可能与调用外部搜索引擎相比)。 invokeAll()可以为你管理线程。
  2. 你可能不想混合使用数组和泛型。
  3. 您正在调用aggregatedResults.add()而不是addAll()。
  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; 
    } 
} 
+0

更改为缓存线程池可能不是最好的选择,因为您的应用程序是IO绑定的,因为大多数搜索引擎速度非常快并且会立即响应。 – akarnokd 2009-07-19 15:27:23

+0

@ kd304:的确,我使用的搜索引擎速度很快(目前Google和Yahoo)。但是,我使用了很多查询,因此需要并发性。你对此有何建议?从我读过的newCachedThreadPool方法的javadoc中,它似乎符合我的目的。但是再次,我对这个API很陌生。 – 2009-07-19 15:39:36

+0

@Avi:非常感谢您的建议! – 2009-07-19 15:40:37

4

作为futher改进,你可以考虑使用一个CompletionService 它分离提交和检索,而不是把所有的未来结果从中你把结果在他们完成订单队列的顺序..

+0

由于应用程序只能在* every *任务完成后在此情况下继续,因此CompletionService在此处可能不合适。 – Avi 2009-07-19 15:26:47

+0

@Avi:我不同意,这只是未来的美好。得到()。 – akarnokd 2009-07-19 15:28:40

+0

@ kd304:您将使用什么方法的CompletionService来获取一组任务的所有结果? – Avi 2009-07-19 15:40:34

3

我可以建议你使用Future.get() with a timeout

否则它会只需要一个搜索引擎不响应使一切都停止(它甚至不需要是一个搜索引擎的问题,比如说,如果你方有一个网络问题)

+0

谢谢。这种操作使用的典型超时值是多少? – 2009-07-19 16:31:49

+0

我想你需要问问自己,你准备等待多久:-)使其可配置并将其设置为(比如说)10倍的正常响应时间。 – 2009-07-19 16:32:45

+0

我认为超时代码中的正确层不是Future.get(),它是网络(HTTP?)对搜索引擎本身的调用。如果搜索引擎超时,那么应该抓住它,并且不要捆绑不再需要的线程。 – Avi 2009-07-20 09:25:05