熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

Heritrix的多線程ToeThread和ToePool

2022-06-13   來源: Java開源技術 

  想要更有效更快速的抓取網頁內容則必須采用多線程Heritrix中提供了一個標准的線程池ToePool它用於管理所有的抓取線程 ToePool和ToeThread都位於orgarchivecrawlerframework包中前面已經說過ToePool的初始化是在CrawlController的initialize()方法中完成的來看一下ToePool以及ToeThread是如何被初始化的以下代碼是在CrawlController中用於對ToePool進行初始化的
    構造函數 toePool = new ToePool(this);
    // 按orderxml中的配置實例化並啟動線程
    toePoolsetSize(ordergetMaxToes()); ToePool的構造函數很簡單如下所示 public ToePool(CrawlController c) {
     super(ToeThreads);
     ntroller = c;
    } 它僅僅是調用了父類javalangThreadGroup的構造函數同時將注入的CrawlController賦給類變量這樣便建立起了一個線程池的實例了但是那些真正的工作線程又是如何建立的呢?
    下面來看一下線程池中的setSize(int)方法從名稱上看這個方法很像是一個普通的賦值方法但實際上它並不是那麼簡單 public void setSize(int newsize)
    {
     targetSize = newsize;
     int difference = newsize getToeCount();

  // 如果發現線程池中的實際線程數量小於應有的數量
     // 則啟動新的線程
     if (difference > ) {
          for(int i = ; i <= difference; i++) {
           // 啟動新線程
              startNewThread();
      }
     }
     // 如果線程池中的線程數量已經達到需要
     else
     {

  int retainedToes = targetSize;
          // 將線程池中的線程管理起來放入數組中
          Thread[] toes = thisgetToes();

  // 循環去除多余的線程
          for (int i = ; i < toeslength ; i++) {
              if(!(toes[i] instanceof ToeThread)) {
                   continue;
              }
              retainedToes;
              if (retainedToes>=) {
                   continue;
              }
              ToeThread tt = (ToeThread)toes[i];
              ttretire();
          }
     }
    }

  // 用於取得所有屬於當前線程池的線程
    private Thread[] getToes()
    {
     Thread[] toes = new Thread[activeCount()+];
     // 由於ToePool繼承自javalangThreadGroup類
     // 因此當調用enumerate(Thread[] toes)方法時
     // 實際上是將所有該ThreadGroup中開辟的線程放入
     // toes這個數組中以備後面的管理
     thisenumerate(toes);
     return toes;
    }

  // 開啟一個新線程
    private synchronized void startNewThread()
    {
     ToeThread newThread = new ToeThread(this nextSerialNumber++);
     newThreadsetPriority(DEFAULT_TOE_PRIORITY);
     newThreadstart();
    } 通過上面的代碼可以得出這樣的結論線程池本身在創建的時候並沒有任何活動的線程實例只有當它的setSize方法被調用時才有可能創建新線程如果當setSize方法被調用多次而傳入不同的參數時線程池會根據參數裡所設定的值的大小來決定池中所管理線程數量的增減


    當線程被啟動後所執行的是其run()方法中的片段接下來看一個ToeThread到底是如何處理從Frontier中獲得的鏈接的 public void run()
    {
     String name = controllergetOrder()getCrawlOrderName();
     loggerfine(getName()+ started for order +name+);

  try {
      while ( true )
      {
              // 檢查是否應該繼續處理
       continueCheck();
       setStep(STEP_ABOUT_TO_GET_URI);
              // 使用Frontier的next方法從Frontier中
              // 取出下一個要處理的鏈接
              CrawlURI curi = controllergetFrontier()next();
              // 同步當前線程
              synchronized(this) {
                  continueCheck();
                  setCurrentCuri(curi);
              }

  /*
               * 處理取出的鏈接
               */
              processCrawlUri();
              setStep(STEP_ABOUT_TO_RETURN_URI);
              // 檢查是否應該繼續處理
              continueCheck();
              // 使用Frontier的finished()方法
              // 來對剛才處理的鏈接做收尾工作
              // 比如將分析得到的新的鏈接加入
              // 到等待隊列中去
              synchronized(this) {
                  controllergetFrontier()finished(currentCuri);
                  setCurrentCuri(null);
              }

  // 後續的處理
              setStep(STEP_FINISHING_PROCESS);
              lastFinishTime = SystemcurrentTimeMillis();
          // 釋放鏈接
              controllerreleaseContinuePermission();
              if(shouldRetire) {
                  break; // from while(true)
              }
          }
     } catch (EndedException e) {
     } catch (Exception e) {
         loggerlog(LevelSEVEREFatal exception in +getName()e);
     } catch (OutOfMemoryError err) {
         seriousError(err);
     } finally {
         controllerreleaseContinuePermission();
     }
     setCurrentCuri(null);

  // 清理緩存數據
     this();
     thishttpRecorder = null;
     localProcessors = null;

  loggerfine(getName()+ finished for order +name+);
     setStep(STEP_FINISHED);
     controllertoeEnded();
     controller = null;
    } 在上面的方法中很清楚的顯示了工作線程是如何從Frontier中取得下一個待處理的鏈接然後對鏈接進行處理並調用Frontier的finished方法來收尾釋放鏈接最後清理緩存終止單步工作等另外其中還有一些日志操作主要是為了記錄每次抓取的各種狀態 很顯然以上代碼中最重要的一行語句processCrawlUri()它是真正調用處理鏈來對鏈接進行處理的代碼


From:http://tw.wingwit.com/Article/program/Java/ky/201311/28205.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.