2009-07-19 132 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