2014-10-14 2 views
0

Вот оригинальный веб-гусеничный, в котором я написал: (Только для справки)Реализация нитей в Java Web Crawler

https://github.com/domshahbazi/java-webcrawler/tree/master 

Это простой веб-обходчик, который посещает заданную начальную веб-страницу, царапанье все ссылки со страницы и добавляет их в очередь (LinkedList), где они затем вылетают один за другим и каждый посетил, где цикл начинается снова. Чтобы ускорить мою программу и для обучения, я попытался реализовать потоки, поэтому я мог бы сразу запустить много потоков, индексируя больше страниц за меньшее время. Ниже каждый класс:

Основной класс

public class controller { 

    public static void main(String args[]) throws InterruptedException { 

     DataStruc data = new DataStruc("http://www.imdb.com/title/tt1045772/?ref_=nm_flmg_act_12"); 

     Thread crawl1 = new Crawler(data); 
     Thread crawl2 = new Crawler(data); 

     crawl1.start(); 
     crawl2.start(); 
    }  
} 

Гусеничный класса (Thread)

public class Crawler extends Thread { 

    /** Instance of Data Structure **/ 
    DataStruc data; 

    /** Number of page connections allowed before program terminates **/ 
    private final int INDEX_LIMIT = 10; 

    /** Initial URL to visit **/ 
    public Crawler(DataStruc d) { 
     data = d; 
    } 

    public void run() { 

     // Counter to keep track of number of indexed URLS 
     int counter = 0; 

     // While URL's left to visit 
     while((data.url_to_visit_size() > 0) && counter<INDEX_LIMIT) { 

      // Pop next URL to visit from stack 
      String currentUrl = data.getURL(); 

      try { 
       // Fetch and parse HTML document 
       Document doc = Jsoup.connect(currentUrl)     
         .userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36") 
         .referrer("http://www.google.com") 
         .timeout(12000) 
         .followRedirects(true) 
         .get(); 

       // Increment counter if connection to web page succeeds 
       counter++; 

       /** .select returns a list of elements (links in this case) **/ 
       Elements links = doc.select("a[href]"); // Relative URL 

       // Add newly found links to stack 
       addLinksToQueue(links);        

      } catch (IOException e) { 
       //e.printStackTrace(); 
       System.out.println("Error: "+currentUrl); 
      }    
     }  
    } 

    public void addLinksToQueue(Elements el) { 
     // For each element in links 
     for(Element e : el) {   

      String theLink = e.attr("abs:href"); // 'abs' prefix ensures absolute url is returned rather then relative url ('www.reddit.com/hello' rather then '/hello') 

      if(theLink.startsWith("http") && !data.oldLink(theLink)) { 
       data.addURL(theLink); 
       data.addVisitedURL(theLink); // Register each unique URL to ensure it isnt stored in 'url_to_visit' again 
       System.out.println(theLink); 
      }    
     } 
    } 
} 

DataStruc Класс

public class DataStruc { 

    /** Queue to store URL's, can be accessed by multiple threads **/ 
    private ConcurrentLinkedQueue<String> url_to_visit = new ConcurrentLinkedQueue<String>(); 

    /** ArrayList of visited URL's **/ 
    private ArrayList<String> visited_url = new ArrayList<String>(); 

    public DataStruc(String initial_url) { 
     url_to_visit.offer(initial_url); 
    } 

    // Method to add seed URL to queue 
    public void addURL(String url) { 
     url_to_visit.offer(url); 
    } 

    // Get URL at front of queue 
    public String getURL() { 
     return url_to_visit.poll(); 
    } 

    // URL to visit size 
    public int url_to_visit_size() { 
     return url_to_visit.size(); 
    } 

    // Add visited URL 
    public void addVisitedURL(String url) { 
     visited_url.add(url); 
    } 

    // Checks if link has already been visited 
    public boolean oldLink(String link) { 
     for(String s : visited_url) { 
      if(s.equals(link)) { 
       return true; 
      } 
     } 
     return false; 
    }  
} 

DataStruc - это класс общей структуры данных, к которому будут одновременно обращаться каждый экземпляр потока Crawler.java. DataStruc имеет очередь для хранения ссылок для посещения и администратор для хранения посещенных URL-адресов, чтобы предотвратить ввод цикла. Я использовал ConcurrentLinkedQueue для хранения URL-адресов для посещения, так как я вижу, что он заботится о параллельном доступе. Мне не нужен одновременный доступ с моим arraylist посещаемых URL-адресов, поскольку все, что мне нужно сделать, это добавить к этому и перебрать его, чтобы проверить наличие совпадений.

Моя проблема заключается в том, что когда я сравниваю время работы с использованием одного потока VS, используя 2 потока (по одному и тому же URL), моя однопоточная версия работает быстрее. Я чувствую, что неправильно реализовал потоки, и хотел бы получить некоторые советы, если кто-нибудь сможет определить проблемы?

Спасибо!

+1

Предоставьте текущую реализацию, которую вы используете для многопоточности в виде [SSCCE] (http://sscce.org), иначе мы не сможем вам помочь. Предоставление кода через внешние ссылки неприемлемо на этом сайте. –

+0

Этот вопрос был бы очень странным в это время, но сколько ядер имеет ваш процессор? –

+0

Мой ноутбук двухъядерный –

ответ

1

Добавлено: см мой комментарий, я думаю, что проверка в Crawler

// While URL's left to visit 
     while((data.url_to_visit_size() > 0) && counter<INDEX_LIMIT) { 

неправильно. Вторая нить прекратится сразу же после того, как 1-й поток опросил единственный URL-адрес.

Вы можете игнорировать остальные, но оставил для истории ...

Мой общий подход к таким типам «больших блоков, которые могут работать параллельно» является:

  1. Сделать каждый гусеничный отзывной , Возможно Callable<List<String>>
  2. Разместить их на ExecutorService
  3. Когда они заканчивают, возьмите результаты один в то время и добавить их в список.

Используя эту стратегию, нет необходимости использовать какие-либо параллельные списки вообще. Недостаток заключается в том, что вы не получаете большую прямую обратную связь, поскольку они запущены. И, если то, что они возвращают, огромно, вам может понадобиться беспокоиться о памяти.

Будет ли это соответствовать вашим потребностям? Вам нужно будет беспокоиться о addVisitedURL, поэтому вам все еще нужно это как параллельная структура данных.

Добавлено: Поскольку вы начинаете с одного URL-адреса, эта стратегия не применяется. Вы можете применить его после посещения первого URL-адреса.

+0

* Используя эту стратегию, вам не нужно использовать какие-либо параллельные списки. Недостаток заключается в том, что вы не получаете много обратной связи в реальном времени, поскольку они runnìng *. Первое предложение неверно, поскольку вы можете использовать параллельные списки для подачи данных для задач (в форме «Runnable» или «Callable»). Второе предложение также неверно, y Вам просто нужно определить другую параллельную структуру, совместно используемую для всех задач, где каждая задача может хранить сообщения или сигналы или другой вид информации. Кроме того, если вы видите реализацию OP, вы не получите большую скорость при переходе на стратегию ThreadPool. –

+0

Каждый Callable имеет один URL для обработки, поэтому первое предложение является правильным. Что касается второго предложения, да, вы могли бы определить параллельную структуру сообщений, но, если вы этого не сделаете, жизнь будет проще, но будет меньше обратной связи. Однако, поскольку искатели OP взаимодействуют друг с другом, этот подход может быть менее оптимальным. – user949300

0
class controller { 

    public static void main(String args[]) throws InterruptedException { 

     final int LIMIT = 4; 

     List<String> seedList = new ArrayList<>(); //1 
     seedList.add("https://www.youtube.com/"); 
     seedList.add("https://www.digg.com/"); 
     seedList.add("https://www.reddit.com/"); 
     seedList.add("https://www.nytimes.com/"); 

     DataStruc[] data = new DataStruc[LIMIT]; 

     for(int i = 0; i < LIMIT; i++){ 
      data[i] = new DataStruc(seedList.get(i)); //2 
     } 

     ExecutorService es = Executors.newFixedThreadPool(LIMIT); 
     Crawler[] crawl = new Crawler[LIMIT]; 
     for(int i = 0; i < LIMIT; i++){ 
      crawl[i] = new Crawler(data[i]); //3 
     } 

     for(int i = 0; i < LIMIT; i++){ 
      es.submit(crawl[i]) // 4 
     } 
    } 
} 

вы можете попробовать это

  1. создать seedlist

  2. создавать объекты datastruc и добавить seedlist к каждому из них

  3. создать массив ползания и передать datastruc объект к ним один за другим
  4. передать обходной код общему специалисту
Смежные вопросы