2012-01-06 5 views
0

지정된 페이지 수에서 링크 수집기를 프로그래밍 중입니다. 더 효율적으로 만들려면 고정 된 크기의 ThreadPool을 사용하고 있습니다. 내가 정말로 멀티 스레딩 영역의 초보자이기 때문에 문제를 해결하는 데 문제가 있습니다. 그래서 내 아이디어는 모든 스레드가 동일한 일을한다는 것입니다 : 페이지에 연결하고 모든 URL을 수집하십시오. 그 후 url은 다음 스레드를 위해 Queue에 추가됩니다.스레드 풀을 사용하여 웹 페이지에서 링크 수집 java

하지만 작동하지 않습니다. 처음에는 baseurl을 분석하고 URL을 추가하십시오. 하지만 처음에는 LinksToVisit.add (baseurl) 만 실행하고 스레드 풀을 사용하여 실행하려고하지만 항상 대기열 및 스레드를 폴링하므로 스레드가 대기열의 맨 위에 새 것을 추가하지 않습니다. null.And 이유는 알 수 없습니다. (

ArrayBlockingQueue하지만 성공하지 못했습니다. 기본 URL 분석으로 해결하는 것은 좋은 해결책이 아닙니다. 왜냐하면 baseurl이 예를 들어 하나의 링크 일 뿐이므로이를 따르지 않기 때문입니다. ... 방법 또는 중요한 뭔가가 HTML 파서으로 내가 사용하고 Jsoup 답변에 대한 감사를

소스 (제거 불필요한 방법) :

package collector; 
import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.text.DecimalFormat; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.Scanner; 
import java.util.Map.Entry; 
import java.util.concurrent.*; 
import org.jsoup.Jsoup; 
import org.jsoup.nodes.Document; 
import org.jsoup.nodes.Element; 
import org.jsoup.select.Elements; 


public class Collector { 

private String baseurl; 
private int links; 
private int cvlinks; 
private double time; 
private int chcount; 
private static final int NTHREADS = Runtime.getRuntime().availableProcessors()*2; 
private ConcurrentLinkedQueue<String> LinksToVisit = new ConcurrentLinkedQueue<String>(); 
private ConcurrentSkipListMap<String, Double> SortedCharMap = new ConcurrentSkipListMap<String, Double>(); 
private ConcurrentHashMap<String, Double> CharMap = new ConcurrentHashMap<String, Double>(); 

public Collector(String url, int links) { 
    this.baseurl = url; 
    this.links = links; 
    this.cvlinks = 0; 
    this.chcount = 0; 

    try { 
     Document html = Jsoup.connect(url).get(); 

     if(cvlinks != links){ 
      Elements collectedLinks = html.select("a[href]"); 
      for(Element link:collectedLinks){ 
       if(cvlinks == links) break; 
       else{ 
        String current = link.attr("abs:href"); 
        if(!current.equals(url) && current.startsWith(baseurl)&& !current.contains("#")){ 
         LinksToVisit.add(current); 
         cvlinks++; 
        } 
       } 
      } 
     } 

     AnalyzeDocument(html, url); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    CollectFromWeb(); 
} 

private void AnalyzeDocument(Document doc,String url){ 
    String text = doc.body().text().toLowerCase().replaceAll("[^a-z]", "").trim(); 
    chcount += text.length(); 
    String chars[] = text.split(""); 
    CharCount(chars); 

} 
private void CharCount(String[] chars) { 
    for(int i = 1; i < chars.length; i++) { 
     if(!CharMap.containsKey(chars[i])) 
      CharMap.put(chars[i],1.0); 
     else 
      CharMap.put(chars[i], CharMap.get(chars[i]).doubleValue()+1); 
    } 
} 

private void CollectFromWeb(){ 
    long startTime = System.nanoTime(); 
    ExecutorService executor = Executors.newFixedThreadPool(NTHREADS); 
    CollectorThread[] workers = new CollectorThread[this.links]; 
    for (int i = 0; i < this.links; i++) { 
     if(!LinksToVisit.isEmpty()){ 
      int j = i+1; 
      System.out.println("Collecting from "+LinksToVisit.peek()+" ["+j+"/"+links+"]"); 
      //Runnable worker = new CollectorThread(LinksToVisit.poll()); 
      workers[i] = new CollectorThread(LinksToVisit.poll()); 
      executor.execute(workers[i]); 
     } 
     else break; 
    } 
    executor.shutdown(); 
    while (!executor.isTerminated()) {} 

    SortedCharMap.putAll(CharMap); 

    this.time =(System.nanoTime() - startTime)*10E-10; 
} 

class CollectorThread implements Runnable{ 
    private Document html; 
    private String url; 

    public CollectorThread(String url){ 
     this.url = url; 
     try { 
      this.html = Jsoup.connect(url).get(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
    @Override 
    public void run() { 
     if(cvlinks != links){ 
      Elements collectedLinks = html.select("a[href]"); 
      for(Element link:collectedLinks){ 
       if(cvlinks == links) break; 
       else{ 
        String current = link.attr("abs:href"); 
        if(!current.equals(url) && current.startsWith(baseurl)&& !current.contains("#")){ 
         LinksToVisit.add(current); 
         cvlinks++; 
        } 
       } 
      } 
     } 

     AnalyzeDocument(html, url); 
    } 
} 

}

답변

1

대신에 LinksToVisit 대기열을 사용하는 대신 CollectorThread.run()에서 직접 executor.execute(new CollectorThread(current))을 호출하면됩니다. ExecutorService에는 스레드가 사용 가능할 때 실행할 고유의 내부 대기열이 있습니다.

또 다른 문제점은 대기열에 첫 번째 URL 집합을 추가 한 후 shutdown()을 호출하면 새 작업이 실행 프로그램에 추가되지 않는다는 것입니다. 큐를 비운 다음 executor를 종료하여 대신이 문제를 해결할 수 있습니다.

class Queue extends ThreadPoolExecutor { 
    Queue(int nThreads) { 
     super(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, 
       new LinkedBlockingQueue<Runnable>()); 
    } 

    protected void afterExecute(Runnable r, Throwable t) { 
     if(getQueue().isEmpty()) { 
      shutdown(); 
     } 
    } 
} 
+0

답변 해 주셔서 감사합니다. 작동하는 것처럼 보이지만 CollectorThread.run() 내부에서 executor.execute (new CollectorThread (current)) 호출을 완전히 이해하는지 모르겠다. 나는 노동자를 만드는 루프를 제거해야합니까? 어떻게 든 재귀 적으로 작동합니까? 감사 – eXPi

관련 문제