2015-05-29 70 views
0

我正在創建一個Web刮取器來從網絡中提取鏈接和電子郵件。這些鏈接將用於查找新的地方以搜索電子郵件,然後將電子郵件存儲在一個集合中。每個鏈接都會在其自己的線程中傳遞給固定線程池以查找更多電子郵件。我從小開始只尋找10封電子郵件,但由於某種原因,我的代碼返回了大約13封電子郵件。執行多於循環邊界的Java多線程

while (emailSet.size() <= EMAIL_MAX_COUNT) { 

     link = linksToVisit.poll(); 

     linksToVisit.remove(link); 
     linksVisited.add(link); 
     pool.execute(new Scraper(link)); 
    } 

    pool.shutdownNow(); 

    emailSet.stream().forEach((s) -> { 
     System.out.println(s); 
    }); 
    System.out.println(emailSet.size()); 

雖然我明白,這是可以創建後,我收到10封電子郵件不應pool.shutdownNow()結束這些線程仍將運行額外的線程?

這是我的線程代碼,如果有幫助。

class Scraper implements Runnable { 

    private String link; 

    Scraper(String s) { 
     link = s; 
    } 

    @Override 
    public void run() { 
     try { 
      Document doc = (Document) Jsoup.connect(link).get(); 

      Elements links = doc.select("a[href]"); 
      for (Element href : links) { 
       String newLink = href.attr("abs:href"); 
       if (!linksVisited.contains(newLink) && !linksToVisit.contains(newLink)) { 
        linksToVisit.add(newLink); 
       } 
      } 
      Pattern p = Pattern.compile(
        "[a-zA-Z0-9_.+-][email protected][a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+"); 
      Matcher matcher = p.matcher(doc.text()); 
      while (matcher.find()) { 
       emailSet.add(matcher.group()); 
      } 
     } catch (Exception e) { 
      //Catch on of the many exceptions Jsoup.connect might throw 
      // and just let the thread expire. 
     } 
    } 
} 

編輯1:

我的這包括我的第一次,但我使用一個線程安全組和隊列。

Set<String> emailSet = Collections.synchronizedSet(new HashSet()); 
BlockingQueue<String> linksToVisit = new ArrayBlockingQueue(10000); 
Set<String> linksVisited = Collections.synchronizedSet(new HashSet()); 
final int EMAIL_MAX_COUNT = 10; 
ExecutorService pool = newFixedThreadPool(25); 

編輯2

計算過,我應該更新我的回答問題所以這裏是我的問題是。

​​

我的列表將從只有一個鏈接開始。第一個鏈接被刪除後,我有一個空列表,不斷創建新的主題,沒有鏈接進行搜索。在列表可以填充之前,我已經創建了數百個線程,但是它們只是減慢系統速度,直​​到最終崩潰。

這裏是代碼修復,以確保如果沒有鏈接搜索,將不會創建線程。

while (emailSet.size() <= EMAIL_MAX_COUNT) { 

     if (linksToVisit.size() > 0) { 
      link = linksToVisit.poll(); 

      linksToVisit.remove(link); 
      linksVisited.add(link); 
      pool.execute(new Scraper(link)); 
      //System.out.println("Emails " + emailSet.size()); 
     } else { 
      try { 
       Thread.sleep(100); 
      } catch (InterruptedException ex) { 
       Logger.getLogger(Crawler.class.getName()) 
         .log(Level.SEVERE, null, ex); 
      } 
     } 
    } 
+0

EMAIL_MAX_COUNT的值是什麼? – Lyrion

+3

你爲什麼要在網上搜索電子郵件地址?我在這裏看到潛在的惡魔! –

+1

EMAIL_MAX_COUNT是10. – mcb

回答

1

你開始一個循環,檢查emailSet尺寸內asyncronous方式scaper,但循環的一個週期的持續時間內刮刀可以找到多個電子郵件,或者您也可以啓動一個以上的刮刀你開始後,它增加了阿拉的電子郵件鏈接的頁面 考慮以下定時

T1 loop start ->T2 loop schedule Scaper ->T3 loop check emailSet ->T4 Scraper finds 13 email -> T5 loop check emailSet 

或以下一個

T1 loop start ->T2 loop schedule Scaper "1" ->T3 loop check emailSet ->T4 loop schedule Scaper "2" T5 -> Scraper "1" finds 6 emails -> T6 loop check emailSet -> Scraper "1" finds 7 emails 

和等等。

如果你想停下來,當你發現10封電子郵件,你必須改變下列一個

while (matcher.find()) { 
    emailSet.add(matcher.group()); 
} 

while (matcher.find()) { 
    if (emailSet.size() <= EMAIL_MAX_COUNT) { 
     emailSet.add(matcher.group()); 
    } 
} 

即使這並不能完全保證你能在EMAIL_MAX_COUNT停止,因爲與多個線程(例如3)可以檢查大小並獲得9,然後他們都會插入一個電子郵件。

如果要確保確切的emailSet大小,您必須同步單個塊(使用synchronized(emailSet)或使用鎖定)中的讀取和寫入操作;像

while (matcher.find()) { 
    synchronized(emailSet) { 
     if (emailSet.size() <= EMAIL_MAX_COUNT) { 
      emailSet.add(matcher.group()); 
     } 
    } 
}