Heritrix的多线程ToeThread和ToePool

4Heritrix的多线程ToeThreadToePool

要想更有效更快捷地抓取网页内容,则必须采用多线程。Heritirx提供了一个标准的线程池ToeThread,用于管理所有的抓取线程。

org.archive.crawler.framework
Class ToePool

java.lang.Object
       
       
  
      
      
       
         
      
      java.lang.ThreadGroup 
      
      

       
          
        
        
  
      
      org.archive.crawler.framework.ToePool

如前所述,ToePool的初始化是在CrawlController的初始化方法中完成的。

private void setupToePool() {

        toePool = new ToePool(this);

       //order.xml中的配置,实例化并启动线程

        toePool.setSize(order.getMaxToes());

}

 

// ToePool构造函数

public ToePool(CrawlController c) {

        super("ToeThreads");

        this.controller = c;

        setDaemon(true);

}

1)真正工作的线程是如何建立的?

从上图可知,其最主要的方法是setSize(int),其源代码如下

public void setSize(int newsize)

    {

        targetSize = newsize;

        int difference = newsize - getToeCount();

       //如果发现线程池中的实际线程数量小于应有的数量则启动新的线程

        if (difference > 0) {

            // 启动新线程

            for(int i = 1; i <= difference; i++) {

                startNewThread();

            }

        }

         //如果线程池中的线程数量已经达到需要

else {

            // must retire extra threads

            int retainedToes = targetSize;

            //将线程池中的线程管理起来放入数组中

            Thread[] toes = this.getToes();

            //循环去除多余的线程

            for (int i = 0; i < toes.length ; i++) {

                if(!(toes[i] instanceof ToeThread)) {

                    continue;

                }

                retainedToes--;

                if (retainedToes>=0) {

                    continue; // this toe is spared

                }

                // otherwise:

                ToeThread tt = (ToeThread)toes[i];

                tt.retire();

            }

        }

}

 

//用于取得所有属于当前线程池的线程

private Thread[] getToes() {

        Thread[] toes = new Thread[activeCount()+10];

        /*

         由于ToePool继承自java.lang.ThreadGroup类,因此当调用enumerateThread[] toes)方法时,实际上时将所有该ThreadGroup中开辟的线程放入toes这个数组中,以备后面的管理

        */

        this.enumerate(toes);

        return toes;

    }

 

//开启一个线程

private synchronized void startNewThread() {

        ToeThread newThread = new ToeThread(this, nextSerialNumber++);

        newThread.setPriority(DEFAULT_TOE_PRIORITY);

        newThread.start();

    }

从上面的代码可知:线程池本身在创建的时候,并没有任何活动的线程实例,只有当它的setSize方法被调用时,才有可能创建新线程;如果当setSize方法被调用多次而传入不同的参数时,线程池会根据参数里所设定的值的大小,来决定池中所管理的线程数量的增减。

2)一个ToeThread到底是如何处理从Frontier中获得的链接?

当线程被启动后,执行的是其run()方法中的片段。

public void run() {

        String name = controller.getOrder().getCrawlOrderName();

        logger.fine(getName()+" started for order '"+name+"'");

 

        try {

            while ( true ) {

                // 检查是否应该继续处理

                continueCheck();

               

                setStep(STEP_ABOUT_TO_GET_URI);

               

                //使用Frontiernext方法从Frontier中取出下一个要处理的链接

                CrawlURI curi = controller.getFrontier().next();

               

                //同步当前线程

                synchronized(this) {

                    continueCheck();

                    setCurrentCuri(curi);

                }

               

                //处理取出的链接

                processCrawlUri();

               

                setStep(STEP_ABOUT_TO_RETURN_URI);

    

                //检查是否应该继续处理

                continueCheck();

 

                /*

                使用Frontierfinished方法来对刚才处理的链接做收尾工作,比如将分析得到的新的链接加入到等级队列中去

                */

                synchronized(this) {

                    controller.getFrontier().finished(currentCuri);

                    setCurrentCuri(null);

                }

               

                //后续的处理

                setStep(STEP_FINISHING_PROCESS);

                lastFinishTime = System.currentTimeMillis();

      

                //释放链接

                controller.releaseContinuePermission();

                if(shouldRetire) {

                    break; // from while(true)

                }

            }

        } catch (EndedException e) {

            // crawl ended (or thread was retired), so allow thread to end

        } catch (Exception e) {

            // everything else (including interruption)

            logger.log(Level.SEVERE,"Fatal exception in "+getName(),e);

        } catch (OutOfMemoryError err) {

            seriousError(err);

        } finally {

            controller.releaseContinuePermission();

        }

        setCurrentCuri(null);

        //清理缓存数据

        this.httpRecorder.closeRecorders();

        this.httpRecorder = null;

        localProcessors = null;

 

        logger.fine(getName()+" finished for order '"+name+"'");

        setStep(STEP_FINISHED);

        controller.toeEnded();

        controller = null;

    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值