使用java ExecutorService线程池,将execute放在while循环和for循环中,执行速度会受影响吗?

SprinLa 2016-08-11 09:41:56
菜鸟求解两个问题:
1.ExecutorService 的使用问题。将es.execute放在两种循环中,看打印的情况,while控制台打印卡顿明显,for中打印的挺流畅的。。。是两种循环方式影响执行速度么?

ExecutorService es = Executors.newFixedThreadPool(1000);
while (true) {
//System.out.println("num="+num++);
es.execute(new Runnable() {
@Override
public void run() {
try {
int ran = (int) (Math.random() * 1000);
System.out.println(Thread.currentThread().getName() + "start,sleep:" + ran);
TimeUnit.MILLISECONDS.sleep(ran);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
ExecutorService es = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000000000; i++) {
es.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "start,sleep:0");
try {
int ran = (int) (Math.random() * 1000);
System.out.println(Thread.currentThread().getName() + "start,sleep:" + ran);
TimeUnit.MILLISECONDS.sleep(ran);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}

2.ExecutorService停止条件
对于ExecutorService.execute的使用很迷糊,是要将它放在无限循环中执行,然后制造一个停止条件吗?我想放在while(true)中,在外部采用一个int计数器,达到1000停止。可是总是无法控制最后的int值,将它设置为volatile,AotuInteger,把判断int型>= 1000时停止的代码块放在同步块中,通通没用,结果有时还是会是1001,1003什么的。请问该如何解决呢?谢谢!




...全文
3076 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
SprinLa 2016-08-13
  • 打赏
  • 举报
回复
引用 14 楼 rickylin86 的回复:
1.感觉不需要设置两种线材.一种就可以了. 2.一台电脑同时运行的线程数是一定的.任务在线程池中会排队运行.所以没必要设置太大. 3.最好能写出你的page文件中一个URL代码块及需要提取出来的结果,然后才知道对应的正则该如何写. PS:不过一般解析html代码会用到Jsoup这个类库.特别对于获取类似标签和属性内容. 可以采用HttpClient + JSoup来处理对应的页面分析和爬取内容
本爬虫目的是提取页面所有有效url,继续爬取,没有对页面的任何处理。 我对页面的处理就是简单的提取出所有的合法url,存入队列,不做其他处理。获取网页用了HttpURLConnection。
URL url = new URL(sUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection(); //建立连接
由于URL url = new URL(sUrl); 要求sUrl必须是完整的url,http://......,我对页面提取是这样处理的; 第一步,用192行的judgeUrl(String sCrawlUrl,String sHtml)函数提取html页面中所有匹配<a></a>的部分,我认为页面中能出现的url必然是在<a></a>中(突然想到<a...../>这种就漏了啊!!)。这一步感觉是有问题的,有时匹配出不知道是什么的东西。 第二部,对得到的内容用214行的checkUrl(String absolutePath, String sUrl)处理: 1.完整url,直接正则匹配出,存入队列; 2.去除无效"url",如js中<a href="javascript:()”> <a href="balabala=<%=....%>”>,或者内容过短明显不是url的; 3.相对路径,"./.....","../...."等,前面补上当前爬取的url。比如当前爬取“http://www.baidu.com/",将它和"./......"两者拼接; 以上几种异常的“url"是我从爬取的url结果发现的,现在又有一种新异常无法解决,如当前爬取的url为“http://www.baidu.com",而不是“http://www.baidu.com"(尾巴上没有/),当前处理的url为 asd/dede/de.html,由于不符合上面条件3,会把它直接返回得到asd/dede/de.html,从而出错。 我也在想,会不会还有其他的url异常我没有发现。当前只能是从爬取结果中找出错的url,判断出错类型。 您给出的正则可以正确匹配href中的东西,但不适用于处理异常的url。 我会学习下您说的HttpClient + JSoup,和楼上说的webmagic。对于同样的功能,java给出多种解法,比如单纯下载网页就有HttpURLConnection和HttpClient可以选择,还有Socket什么的,这么学让我有点迷茫,觉得一个点就要学一千年的样子,怎样才算学会了某个知识点呢?
SprinLa 2016-08-13
  • 打赏
  • 举报
回复
引用 13 楼 u012171905 的回复:
推荐java实现的优雅的爬虫框架...webmagic...源码在github
谢谢!我看了一下,貌似很火。。。我学java有段时间了,但是一直没有做项目,以为自己学会了,但是一遇到问题就解决不了,所以想从头做做项目,做了个简单爬虫就看出了自己的水平,囧。。。
SprinLa 2016-08-12
  • 打赏
  • 举报
回复
引用 7 楼 rickylin86 的回复:
你需要考虑的同步操作应该是对URL列表的维护。 比如列表的添加URL和提取URL(如果存在多个parse线程的话). 可以考虑用指针来操作提取下一个未解析过的URL
非常感谢您的详细回答!代码我认真看了,理解了您的思路,利用一个ArrayList存储结果,在外部进行MAXCRAWLEDTIME大循环,保证爬取的url个数为MAXCRAWLEDTIME,即保证创建MAXCRAWLEDTIME个任务,并把爬取网页和分析网页都交给ParseTask来完成。您的代码里我学习到了ParseTask.class.notifyAll()和ParseTask.class.wait()的用法,之前看书觉得迷糊,我这里理解您是用用它来为ArrayList提供生产者-消费者功能吧!同时91行的for循环也让我觉得很精妙...受益匪浅! 由于我把爬取网页和分析网页分成了crawler线程和parse线程,所以我把判断终止条件放在parse线程里,用了同步块。 模仿您的代码,我写了我代码的框架部分,一些url处理省略了,用了模拟的url。
package Modify;

import wxy.Utils.MyBloomFilter;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author delia
 * @create 2016-06-28 下午7:55
 */

public class MyCrawler2 {
    private String sUrl = null;
    private int MAX_NUM = 1;
    public static MyBloomFilter mb = new MyBloomFilter();
    public static BlockingQueue<String> urlist = new LinkedBlockingQueue<String>();
    //public static AtomicInteger crawledNum = new AtomicInteger(0);
    private static long crawlNum = 0;
    public static BlockingQueue<PageInfo> pagesList = new LinkedBlockingQueue<PageInfo>();
    private String filePath = null;
    private FileWriter fileWriter = null;
    private BufferedWriter bufferedWriter = null;
    public long startTime = 0;
    private long endTime = 0;
    ExecutorService cachedTPool_GetPage;
    ExecutorService cachedTPool_ParsePage;
    private int offSet = 1;

    public MyCrawler2(String sUrl, int maxNUm, String filePath) {
        this.sUrl = sUrl;
        this.MAX_NUM = maxNUm;
        this.filePath = filePath;
    }

    public static void main(String[] args) {
        int MAX_NUM = 100;
        String filePath = "/Users/delia/Documents/workspace/Crawler/out/urlResult.txt";//存储结果
        MyCrawler2 mc = new MyCrawler2("url-1",MAX_NUM,filePath);
        mc.startTime = System.currentTimeMillis();
        mc.crawl();
    }

    //爬取方法
    public void crawl() {
        try {
            this.fileWriter = new FileWriter(filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.bufferedWriter = new BufferedWriter(fileWriter);
        cachedTPool_GetPage = Executors.newFixedThreadPool(100);
        cachedTPool_ParsePage = Executors.newFixedThreadPool(10);

        try {
            System.out.println("爬虫开始!入口url为:"+sUrl);
            urlist.put(sUrl);
            cachedTPool_GetPage.submit(new TaskCrawlPages());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    // 抓取页面线程
    class TaskCrawlPages implements Runnable {
        @Override
        public void run() {
            //System.out.println(Thread.currentThread().getName() + "crawler网页");
            crawlPage();//抓取网页
        }
    }

    //分析页面线程
    class TaskParsePages implements Runnable {
        @Override
        public void run() {
            //System.out.println(Thread.currentThread().getName() + "parse网页");
            parsePage();//分析网页,提取url
        }
    }

    public void crawlPage() {
        try {
            String sUrl = urlist.take();
            /*sUrl,下载网页代码,对响应码为200的url,保存其网页等信息,存入队列
             * sUrl:当前爬取的url
             * sHtml:网页内容
             * len:网页长度
            */
            TimeUnit.MILLISECONDS.sleep((int)(Math.random()*100));//模拟执行时间
            String sHtml = "本应获取的网页内容;";
            PageInfo pageInfo = new PageInfo(sUrl, sHtml.toString(), 0);
            pagesList.put(pageInfo);
            cachedTPool_ParsePage.submit(new TaskParsePages());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void parsePage() {
        PageInfo pageInfo = null;
        String sHtml = null;
        String sCrawlUrl = null;
        synchronized (TaskCrawlPages.class) {
            if (crawlNum >= MAX_NUM) {
                cachedTPool_GetPage.shutdownNow();
                cachedTPool_ParsePage.shutdownNow();
                endTime = System.currentTimeMillis();
                System.out.println("======================已爬取 " + crawlNum + " 个,程序终止,执行时间:" + (endTime - startTime) / 1000.0 + "s");
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.exit(1);
                }
                System.exit(0);
            }else{
                try {
                    pageInfo = pagesList.take();
                    sCrawlUrl = pageInfo.getsUrl();
                    sHtml = pageInfo.getsHtml();
                    crawlNum ++;
                    System.out.print("当前分析第"+crawlNum+"个,网址为:"+sCrawlUrl+" ");
                    bufferedWriter.write(crawlNum+ "    "+ sCrawlUrl + "\n");
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
            }
        }

        try {
            /*
		    *本应从sHtml中提取url,并用bloomFilter过滤器去除重复url并加入队列
		    *此处省略,模拟分析到了若干个url.
		    */
            TimeUnit.MILLISECONDS.sleep((int)(Math.random()*100));//模拟执行时间
            int urlNum = (int)((Math.random()*10+1));
            System.out.println("爬取到url个数:"+urlNum);
            for (int i = 1; i <= urlNum; i++) {
                String url = sCrawlUrl.replaceAll("^(\\D+)\\d+$", "$1") + (Integer.valueOf(sCrawlUrl.replaceAll("^\\D+", "")) + i);
                //System.out.println("分析出网址:"+url);
                urlist.put(url);
                cachedTPool_GetPage.submit(new TaskCrawlPages());
            }
        }catch (Exception e){}
    }

    class PageInfo {
        String sUrl;//当前爬取的url
        String sHtml;//网页内容
        int contentLen;//网页长度

        public PageInfo(String sUrl, String sHtml,int len) {
            this.sUrl = sUrl;
            this.sHtml = sHtml;
            this.contentLen = len;
        }

        public String getsUrl() {
            return sUrl;
        }

        public void setsUrl(String sUrl) {
            this.sUrl = sUrl;
        }

        public String getsHtml() {
            return sHtml;
        }

        public void setsHtml(String sHtml) {
            this.sHtml = sHtml;
        }

        public int getContentLen() {
            return contentLen;
        }

        public void setContentLen(int contentLen) {
            this.contentLen = contentLen;
        }
    }

    public void judgeUrl(String sCrawlUrl,String sHtml){
            String regex = "<a.*?/a>";//任意字符任意次,尽可能少的匹配
            Pattern pt = Pattern.compile(regex);
            Matcher mt = pt.matcher(sHtml);
            while (mt.find()) {
                Matcher title = Pattern.compile(">.*?</a>").matcher(mt.group());
                Matcher myurl = Pattern.compile("href=[ ]*\".*?\"").matcher(mt.group());
                while (myurl.find()) {
                    String sUrl = myurl.group().replaceAll("href=|>", "").trim();
                    int lenUrl = sUrl.length();
                    sUrl = sUrl.substring(1, lenUrl - 1).replaceAll("\\s*", "");
                    if (sUrl != null && sUrl.length() != 0) {
                        if (checkUrl(sCrawlUrl, sUrl) == null) {
                            //说明不是真正的url
                        }else{
                            //添加到url队列
                        }
                    }
                }
            }
    }

    public String checkUrl(String absolutePath, String sUrl) {
        if (sUrl == null)
            return absolutePath;
        if (sUrl.contains("javascript:") || sUrl.contains("<%=") || sUrl.indexOf("'") == 0 || sUrl.length() <= ("https://".length() + 1))
            return null;
        if (!sUrl.contains("http://") && !sUrl.contains("https://")) {
            if (sUrl.indexOf("/") == 0 || sUrl.indexOf("../") == 0 || sUrl.indexOf("./") == 0) {
                URL absoluteUrl, parseUrl;
                try {
                    absoluteUrl = new URL(absolutePath);
                    parseUrl = new URL(absoluteUrl, sUrl);
                    return parseUrl.toString();

                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            } else {
                return "http://" + sUrl;
            }
        }
        return sUrl;
    }
}
以上,我感觉是解决了终止条件的问题,可以精确终止。接下来有一些疑点: 1.第45行,我需要用两个线程池开启两种线程吗?还是用一个线程池就好呢? 2.我开启两个固定长度线程池时,池大小和要爬取的总数会影响执行效率。我随机设置的,因为parse线程执行一次便可产生很多url,我便将它的线程数设定少一些。设定后,爬取100个,和爬取10000个的效率是不同的。线程池的数量到底该如何把控呢? 3.url的正则表达式匹配问题。我的算法是192行的judgeUrl(String sCrawlUrl,String sHtml)及214行的checkUrl(String absolutePath, String sUrl)协作匹配,可是实际中匹配出了“./ssid=0/from=0/bd_page_type=1/uid=6886A5ACD3577F8E7251677EAF4EDAE0/t=news/tc?order=1&pfr=3-11-bdindex-top-10--&m=0&src=http://wap.hexun.com/2.0/stock_info_hq.jsp?stockcode=1000001 ”这样的“合法”url(没有检测出不合法),导致下载网页时报出MalformedURLException异常。 谢谢!
rickylin86 2016-08-12
  • 打赏
  • 举报
回复

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test{
	public static void main(String[] args){
		String url = "<a target='_blank' src1='value1' href='http://www.baidu.com' src2='value2' src3=\"value3\">  点击前往百度   </a>";
		String regex = "<a(?:\\s+\\S+\\s*=\\s*(['\"]).*?\\1)*\\s+href\\s*=(['\"])(?<url>.*?)\\2(?:\\s+\\S+\\s*=\\s*(['\"]).*?\\4)*\\s*>(?<title>[^<]+)</a>";
		Matcher matcher = Pattern.compile(regex).matcher(url);
		if(matcher.find()){
			System.out.printf("url:'%s'\n",matcher.group("url"));
			System.out.printf("title:'%s'\n",matcher.group("title").trim());
		}
	}
}
rickylin86 2016-08-12
  • 打赏
  • 举报
回复
1.感觉不需要设置两种线材.一种就可以了. 2.一台电脑同时运行的线程数是一定的.任务在线程池中会排队运行.所以没必要设置太大. 3.最好能写出你的page文件中一个URL代码块及需要提取出来的结果,然后才知道对应的正则该如何写. PS:不过一般解析html代码会用到Jsoup这个类库.特别对于获取类似标签和属性内容. 可以采用HttpClient + JSoup来处理对应的页面分析和爬取内容
Coder_D 2016-08-12
  • 打赏
  • 举报
回复
推荐java实现的优雅的爬虫框架...webmagic...源码在github
SprinLa 2016-08-11
  • 打赏
  • 举报
回复
引用 2 楼 rickylin86 的回复:
1 .ExecutorService并不是这么用的.特别是你创建固定容量的时候,没必要创建那么大容量. 按照你上面的代码while之所以顿挫是因为你无限的添加任务给执行器.如果你要设置一个变量来控制添加到执行器中任务的数量的话没必要考虑volatile,AotuInteger.因为本身执行器添加任务只在main中操作,其他的线程并不会影响到控制次数变量i;直接

while(true){
			if(i++ >= 10){
				break;
			}
      ....other code...
}
executor.shutdown();
即可.
谢谢! 问题1,一般的固定容量设置为多少呢?我开始想用newCachedThreadPool做测试用例,但是循环次数多了或者单个线程执行时间长了就会OutOfMemory,头疼。 问题2,终止条件问题,您说的那种我明白,我想做的应用不是以开启线程数为终止条件的。 我是想做爬虫,两种线程,一种是crawler线程,来爬取url网页,从url队列提取url获得网页,并把爬到的网页内容存到page队列;另一种是parse线程,从上述page队列提取出url,存到url队列,如此反复。我用线程池开启这两种任务,放在while(true)中,终止条件不是开启的线程数,而是爬取url数,这个数量是在具体任务中改变的,所以需要同步。但是我的同步做不来,同步控制块,AutoInteger等都试过,总是无法精确控制。请问这种问题该如何解决呢?具体代码如下: 先在外部定义了public static AtomicInteger crawledNum = new AtomicInteger(0);//爬取数目crawledNum作为终止条件 在线程里进行判断 synchronized (crawledNum) { numC = crawledNum.get(); System.out.println(Thread.currentThread().getName()+" 已爬取="+numC); if (numC >= MAX_NUM) {//终止条件 try { cachedTPool_GetPage.shutdownNow(); cachedTPool_ParsePage.shutdownNow(); endTime = System.currentTimeMillis(); System.out.println("task-" + threadIndex + ":已爬取 " + numC + " 个,程序终止,执行时间:" + (endTime - startTime) / 1000.0 + "s"); //System.exit(0); bwResult.close(); writerResult.close(); } catch (IOException e) { e.printStackTrace(); } finally { System.exit(0); } } } crawledNum.addAndGet(1);//若未终止,继续执行。 ...... 如上,爬取数目crawledNum总是无法精确控制到crawledNum。
SprinLa 2016-08-11
  • 打赏
  • 举报
回复
引用 1 楼 youyaecho 的回复:
同时发现,如果用for循环,循环次数也影响打印的流畅度,难道循环次数也影响线程池的执行速度么?
谢谢! 问题1,一般的固定容量设置为多少呢?我开始想用newCachedThreadPool做测试用例,但是循环次数多了或者单个线程执行时间长了就会OutOfMemory,头疼。 问题2,终止条件问题,您说的那种我明白,我想做的应用不是以开启线程数为终止条件的。 我是想做爬虫,两种线程,一种是crawler线程,来爬取url网页,从url队列提取url获得网页,并把爬到的网页内容存到page队列;另一种是parse线程,从上述page队列提取出url,存到url队列,如此反复。我用线程池开启这两种任务,放在while(true)中,终止条件不是开启的线程数,而是爬取url数,这个数量是在具体任务中改变的,所以需要同步。但是我的同步做不来,同步控制块,AutoInteger等都试过,总是无法精确控制。请问这种问题该如何解决呢?具体代码如下: 先在外部定义了public static AtomicInteger crawledNum = new AtomicInteger(0);//爬取数目crawledNum作为终止条件 在线程里进行判断 synchronized (crawledNum) { numC = crawledNum.get(); System.out.println(Thread.currentThread().getName()+" 已爬取="+numC); if (numC >= MAX_NUM) {//终止条件 try { cachedTPool_GetPage.shutdownNow(); cachedTPool_ParsePage.shutdownNow(); endTime = System.currentTimeMillis(); System.out.println("task-" + threadIndex + ":已爬取 " + numC + " 个,程序终止,执行时间:" + (endTime - startTime) / 1000.0 + "s"); //System.exit(0); bwResult.close(); writerResult.close(); } catch (IOException e) { e.printStackTrace(); } finally { System.exit(0); } } } crawledNum.addAndGet(1);//若未终止,继续执行。 ...... 如上,爬取数目crawledNum总是无法精确控制到crawledNum。
rickylin86 2016-08-11
  • 打赏
  • 举报
回复
1 .ExecutorService并不是这么用的.特别是你创建固定容量的时候,没必要创建那么大容量. 按照你上面的代码while之所以顿挫是因为你无限的添加任务给执行器.如果你要设置一个变量来控制添加到执行器中任务的数量的话没必要考虑volatile,AotuInteger.因为本身执行器添加任务只在main中操作,其他的线程并不会影响到控制次数变量i;直接

while(true){
			if(i++ >= 10){
				break;
			}
      ....other code...
}
executor.shutdown();
即可.
SprinLa 2016-08-11
  • 打赏
  • 举报
回复
同时发现,如果用for循环,循环次数也影响打印的流畅度,难道循环次数也影响线程池的执行速度么?
rickylin86 2016-08-11
  • 打赏
  • 举报
回复
上面代码有点小bug.下面代码是已经修改好的版本. 并且第40行提供一个模拟测试,可以直接运行查看结果. 大概的构思是 提交ParseTask给执行器.并且只能提交MAX次. 而ParseTask中的参数只能从之前的ParseTask任务中获取.

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import java.util.ArrayList;

/*
*注:ParseTask.call()方法获取页面URL未实现..
*/
public class Test{
	public static void main(String[] args){
		int poolSize = 2;
		String initURL = "url 1";//设置初始url
		TaskManager manager = new TaskManager(poolSize,initURL);
		manager.showResults();
	}
}

class ParseTask implements Callable<ArrayList<String>>{

	public ParseTask(String url){
		this.url = url;
	}


	@Override
	public ArrayList<String> call(){
		ArrayList<String> urls = new ArrayList<>();
		//process to get the in urls in page(url)
		/*
		*这里需要实现. To be continue ...
		*这里放置获取页面url的代码,并添加到urls中
		*
		*/

		//以下代码模拟每个页面获取到一个区别于results列表中元素的新url

		urls.add(url.replaceAll("^(\\D+)\\d+$","$1") + (Integer.valueOf(url.replaceAll("^\\D+","")) + 1));


		synchronized(ParseTask.class){
			ArrayList<String> results = TaskManager.getResults();
			for(String url : urls){
				//将该页面获取到的url过滤并放入results中.
				if(!results.contains(url)){
					results.add(url);
				}
			}
			//通知TaskManager继续生产ParseTask线程并放入执行器中
			ParseTask.class.notifyAll();
		}
		return urls;
	}

	
	private String url;
}

class TaskManager{
	public TaskManager(int poolSize,String initURL){
		executor = Executors.newFixedThreadPool(poolSize);
		task = new ParseTask(initURL);
		try{
			results = executor.submit(task).get();
		}catch(InterruptedException|ExecutionException e){
			e.printStackTrace();
			System.exit(1);
		}
		
	}

	private void start(){
		ArrayList<ParseTask> tasks = null;
		for(int crawledTime = 0 ; crawledTime < MAXCRAWLEDTIME ;){
			//假设最终results.size() > MAXCRAWLEDTIME
			//执行MAXCRAWLEDTIME次爬虫任务
			if(crawledTime == results.size()){
				//当解析到results最后一个元素的时候等待.
				synchronized(ParseTask.class){
					try{
						ParseTask.class.wait();
					}catch(InterruptedException e){
						e.printStackTrace();
						System.exit(1);
					}
				}
			}
			tasks = new ArrayList<>();
			for(; crawledTime < results.size() && crawledTime < MAXCRAWLEDTIME; crawledTime ++){
				tasks.add(new ParseTask(results.get(crawledTime)));
			}
			try{
				executor.invokeAll(tasks);
			}catch(InterruptedException e){
				e.printStackTrace();
				System.exit(1);
			}
		}
		executor.shutdown();
	}

	public void showResults(){
		//用于执行完毕后显示结果.
		start();
		try{
			executor.awaitTermination(1,TimeUnit.DAYS);
		}catch(InterruptedException e){
			e.printStackTrace();
			System.exit(1);
		}

		for(String url : results){
			System.out.println(url);
		}

	}

	public static ArrayList<String> getResults(){
		return results;
	}


	private ExecutorService executor;
	private ParseTask task;
	private static ArrayList<String> results = new ArrayList<>();
	private static final int MAXCRAWLEDTIME = 1000;
}
rickylin86 2016-08-11
  • 打赏
  • 举报
回复

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import java.util.ArrayList;

/*
*注:ParseTask.call()方法获取页面URL未实现..
*/
public class Test{
	public static void main(String[] args){
		int poolSize = 2;
		String initURL = "";//设置初始url
		TaskManager manager = new TaskManager(poolSize,initURL);
		manager.showResults();
	}
}

class ParseTask implements Callable<ArrayList<String>>{

	public ParseTask(String url){
		this.url = url;
	}


	@Override
	public ArrayList<String> call(){
		ArrayList<String> urls = new ArrayList<>();
		//process to get the in urls in page(url)
		/*
		*这里需要实现. To be continue ...
		*这里放置获取页面url的代码,并添加到urls中
		*
		*/
		synchronized(ParseTask.class){
			ArrayList<String> results = TaskManager.getResults();
			for(String url : urls){
				//将该页面获取到的url过滤并放入results中.
				if(!results.contains(url)){
					results.add(url);
				}
			}
			//通知TaskManager继续生产ParseTask线程并放入执行器中
			ParseTask.class.notifyAll();
		}
		return urls;
	}

	
	private String url;
}

class TaskManager{
	public TaskManager(int poolSize,String initURL){
		executor = Executors.newFixedThreadPool(poolSize);
		task = new ParseTask(initURL);
		try{
			results = executor.submit(task).get();
		}catch(InterruptedException|ExecutionException e){
			e.printStackTrace();
			System.exit(1);
		}
		
	}

	private void start(){
		ArrayList<ParseTask> tasks = null;
		for(int crawledTime = 0 ; crawledTime < MAXCRAWLEDTIME ;){
			//假设最终results.size() > MAXCRAWLEDTIME
			//执行MAXCRAWLEDTIME次爬虫任务
			if(crawledTime == results.size()){
				//当解析到results最后一个元素的时候等待.
				synchronized(ParseTask.class){
					try{
						ParseTask.class.wait();
					}catch(InterruptedException e){
						e.printStackTrace();
						System.exit(1);
					}
				}
			}
			tasks = new ArrayList<>();
			for(; crawledTime < results.size() && crawledTime < MAXCRAWLEDTIME; crawledTime ++){
				tasks.add(new ParseTask(results.get(crawledTime)));
			}
			try{
				executor.invokeAll(tasks);
			}catch(InterruptedException e){
				e.printStackTrace();
				System.exit(1);
			}
		}
		executor.shutdown();
	}

	public void showResults(){
		//用于执行完毕后显示结果.
		start();
		try{
			executor.awaitTermination(1,TimeUnit.DAYS);
		}catch(InterruptedException e){
			e.printStackTrace();
			System.exit(1);
		}

		for(String url : results){
			System.out.println(url);
		}

	}

	public static ArrayList<String> getResults(){
		return results;
	}


	private ExecutorService executor;
	private ParseTask task;
	private static ArrayList<String> results;
	private static final int MAXCRAWLEDTIME = 1000;
}
SprinLa 2016-08-11
  • 打赏
  • 举报
回复
引用 6 楼 rickylin86 的回复:
像上面的这样的处理方式有很大几率出现两种可能性. 1.死循环.比如 URLA>PAGEA(URLA,URLB) 这样URL队列中总会不断的存在URLA需要处理.当然这种情况可以通过处理来排除. 2.无限循环.比如从PAGE中获取的URL都跟URL列表中的不一样.那么就有不断的URL产生.最终结果是导致内存不足.需要借助外部存储和读取来解决.
parse线程图画错了,是提取页面所有的url
SprinLa 2016-08-11
  • 打赏
  • 举报
回复
引用 6 楼 rickylin86 的回复:
像上面的这样的处理方式有很大几率出现两种可能性.
1.死循环.比如 URLA>PAGEA(URLA,URLB) 这样URL队列中总会不断的存在URLA需要处理.当然这种情况可以通过处理来排除.
2.无限循环.比如从PAGE中获取的URL都跟URL列表中的不一样.那么就有不断的URL产生.最终结果是导致内存不足.需要借助外部存储和读取来解决.



差不多是这样子的,用了两个BlockingQueue作为队列,有数据则执行,无数据则阻塞线程。对于您说的两个问题:
1.死循环问题用布控过滤器去重。
2,我目前也没有方法,能想到的就是crawler线程开启多一些,parse线程开小一些,这样子让新生成的url速度相对来说变慢。由这个问题衍生出了ExecutorService的提问,不知道是不是可以把execute()方法放在while(true)循环中,因为我实际用线程池比用单独几个线程在run()方法中循环处理还要慢,找不到原因很头大;同时线程终止条件设置成分析过的网页数,结果这个终止条件判断不好,由这个引申出上面AutoInteger等无法解决终止条件的问题。
希望能给个思路,谢谢!(PS:网上说爬虫很好写...我做这点就用了一个月,而且出来还是渣渣...)
rickylin86 2016-08-11
  • 打赏
  • 举报
回复
你需要考虑的同步操作应该是对URL列表的维护。 比如列表的添加URL和提取URL(如果存在多个parse线程的话). 可以考虑用指针来操作提取下一个未解析过的URL
rickylin86 2016-08-11
  • 打赏
  • 举报
回复
像上面的这样的处理方式有很大几率出现两种可能性. 1.死循环.比如 URLA>PAGEA(URLA,URLB) 这样URL队列中总会不断的存在URLA需要处理.当然这种情况可以通过处理来排除. 2.无限循环.比如从PAGE中获取的URL都跟URL列表中的不一样.那么就有不断的URL产生.最终结果是导致内存不足.需要借助外部存储和读取来解决.
rickylin86 2016-08-11
  • 打赏
  • 举报
回复

你需要的运算逻辑是上面图片显示的那样吗?从URL获取page,然后从page不断的添加url.然后当已经处理的URL达到一个设定的最大值的时候就不再继续执行.是吗?

50,523

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧