Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 服务永不停止。在另一个正在执行的任务中执行新任务时_Java_Multithreading_Jsoup_Executorservice_Executors - Fatal编程技术网

Java 服务永不停止。在另一个正在执行的任务中执行新任务时

Java 服务永不停止。在另一个正在执行的任务中执行新任务时,java,multithreading,jsoup,executorservice,executors,Java,Multithreading,Jsoup,Executorservice,Executors,你好 我的网络爬虫项目有拦截器问题。 逻辑很简单。首先创建一个Runnable,它下载html文档,扫描所有链接,然后在所有资助链接上创建新的Runnable对象。每个新创建的Runnable依次为每个链接创建新的Runnable对象并执行它们 问题是执行器服务从不停止 CrawlerTest.java public class CrawlerTest { public static void main(String[] args) throws InterruptedExceptio

你好

我的网络爬虫项目有拦截器问题。 逻辑很简单。首先创建一个
Runnable
,它下载html文档,扫描所有链接,然后在所有资助链接上创建新的
Runnable
对象。每个新创建的
Runnable
依次为每个链接创建新的
Runnable
对象并执行它们

问题是执行器服务从不停止

CrawlerTest.java

public class CrawlerTest {

    public static void main(String[] args) throws InterruptedException {
        new CrawlerService().crawlInternetResource("https://jsoup.org/");
    }
}
CrawlerService.java

import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class CrawlerService {

    private Set<String> uniqueUrls = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(10000));
    private ExecutorService executorService = Executors.newFixedThreadPool(8);
    private String baseDomainUrl;

    public void crawlInternetResource(String baseDomainUrl) throws InterruptedException {
        this.baseDomainUrl = baseDomainUrl;
        System.out.println("Start");
        executorService.execute(new Crawler(baseDomainUrl)); //Run first thread and scan main domain page. This thread produce new threads.
        executorService.awaitTermination(10, TimeUnit.MINUTES);
        System.out.println("End");
    }

    private class Crawler implements Runnable { // Inner class that encapsulates thread and scan for links

        private String urlToCrawl;

        public Crawler(String urlToCrawl) {
            this.urlToCrawl = urlToCrawl;
        }

        public void run() {
            try {
                findAllLinks();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private void findAllLinks() throws InterruptedException {
            /*Try to add new url in collection, if url is unique adds it to collection, 
             * scan document and start new thread for finded links*/
            if (uniqueUrls.add(urlToCrawl)) { 
                System.out.println(urlToCrawl);

                Document htmlDocument = loadHtmlDocument(urlToCrawl);
                Elements findedLinks = htmlDocument.select("a[href]");

                for (Element link : findedLinks) {
                    String absLink = link.attr("abs:href");
                    if (absLink.contains(baseDomainUrl) && !absLink.contains("#")) { //Check that we are don't go out of domain
                        executorService.execute(new Crawler(absLink)); //Start new thread for each funded link
                    }
                }
            }
        }

        private Document loadHtmlDocument(String internetResourceUrl) {
            Document document = null;
            try {
                document = Jsoup.connect(internetResourceUrl).ignoreHttpErrors(true).ignoreContentType(true)
                        .userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0")
                        .timeout(10000).get();
            } catch (IOException e) {
                System.out.println("Page load error");
                e.printStackTrace();
            }
            return document;
        }
    }
}
import java.io.IOException;
导入java.util.Collections;
导入java.util.Set;
导入java.util.concurrent.ConcurrentHashMap;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.concurrent.TimeUnit;
导入org.jsoup.jsoup;
导入org.jsoup.nodes.Document;
导入org.jsoup.nodes.Element;
导入org.jsoup.select.Elements;
公共类爬虫服务{
私有集uniqueUrls=Collections.newSetFromMap(新ConcurrentHashMap(10000));
私有ExecutorService ExecutorService=Executors.newFixedThreadPool(8);
私有字符串baseDomainUrl;
public void crawlingInternetResource(字符串baseDomainUrl)引发InterruptedException{
this.baseDomainUrl=baseDomainUrl;
系统输出打印项次(“开始”);
executorService.execute(新爬虫程序(baseDomainUrl));//运行第一个线程并扫描主域页。此线程生成新线程。
执行人服务。等待终止(10,时间单位。分钟);
系统输出打印项次(“结束”);
}
私有类爬虫程序实现可运行的{//内部类,该类封装线程并扫描链接
私有字符串urlToCrawl;
公共爬虫(字符串urlToCrawl){
this.urlToCrawl=urlToCrawl;
}
公开募捐{
试一试{
Findallinks();
}捕捉(中断异常e){
e、 printStackTrace();
}
}
私有void findallinks()引发InterruptedException{
/*尝试在集合中添加新url,如果url是唯一的,则将其添加到集合中,
*扫描文档并启动查找链接的新线程*/
if(uniqueUrls.add(urlToCrawl)){
System.out.println(urlToCrawl);
文档htmlDocument=加载htmlDocument(urlToCrawl);
Elements findedLinks=htmlDocument.select(“a[href]”);
对于(元素链接:findedLinks){
字符串absLink=link.attr(“abs:href”);
如果(absLink.contains(baseDomainUrl)&&&!absLink.contains(“#”){//请检查我们是否正在退出域
executorService.execute(新爬虫(absLink));//为每个链接启动新线程
}
}
}
}
私有文档加载HTMLDocument(字符串internetResourceUrl){
单据=空;
试一试{
document=Jsoup.connect(internetResourceUrl).ignoreHttpErrors(true).ignoreContentType(true)
.userAgent(“Mozilla/5.0(Windows NT 6.1;WOW64;rv:48.0)Gecko/20100101 Firefox/48.0”)
.timeout(10000).get();
}捕获(IOE异常){
System.out.println(“页面加载错误”);
e、 printStackTrace();
}
归还文件;
}
}
}
该应用程序需要大约20秒来扫描jsoup.org以查找所有唯一链接。但它只是等待10分钟
executorService.waittermination(10,TimeUnit.minutes)
然后我看到死的主线程和仍然工作的执行器

如何强制执行器服务正常工作

我认为问题在于它调用executorService。在另一个任务中执行,而不是在主线程中执行。

您使用不当。根据javadoc,您应该首先调用
shutdown

阻塞,直到所有任务在关闭请求后完成执行,或超时发生,或当前线程中断(以先发生的为准)


为了实现您的目标,我建议使用(或支持增量的闩锁,如)来确定没有剩余任务的确切时刻,这样您就可以安全地执行
关机操作

您不需要调用关机

这可能会起作用-CrawlerService中的AtomicLong变量。在每个新的子任务提交给executor服务之前的增量

修改run()方法以减少此计数器,如果为0,则关闭executor服务

public void run() {
    try {
        findAllLinks();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        //decrements counter
        //If 0, shutdown executor from here or just notify CrawlerService who would be doing wait().
    }
}

在“finally”中,减少计数器,当计数器为零时,关闭executor或仅通知CrawlerService。0表示,这是最后一个,没有其他正在运行,队列中没有挂起。任何任务都不会提交任何新的子任务。

我看到您之前的评论:


我不能使用倒计时闩锁,因为我事先不知道我将从资源中收集多少唯一链接

首先,vsminkov回答了为什么
awaitterminiation
会坐下来等10分钟。我将提供另一种解决方案

不要使用
倒计时闩锁
而是使用。对于每个新任务,您都可以注册并等待完成

每次执行
时,创建一个单相器和
寄存器
。每次执行
完成时,调用submit
,调用
到达

public void crawlInternetResource(String baseDomainUrl) {
    this.baseDomainUrl = baseDomainUrl;

    Phaser phaser = new Phaser();
    executorService.execute(new Crawler(phaser, baseDomainUrl)); 
    int phase = phaser.getPhase();
    phase.awaitAdvance(phase);
}

private class Crawler implements Runnable { 

    private final Phaser phaser;
    private String urlToCrawl;

    public Crawler(Phaser phaser, String urlToCrawl) {
        this.urlToCrawl = urlToCrawl;
        this.phaser = phaser;
        phaser.register(); // register new task
    }

    public void run(){
       ...
       phaser.arrive(); //may want to surround this in try/finally
    }
如何正确地强制执行器服务工作

我认为问题在于它在另一个任务中而不是在主线程中调用executorService.execute

不,问题不在服务上。您以不正确的方式使用API,因此没有得到正确的结果

1. shutdown
2. awaitTermination
3. shutdownNow
为了得到正确的结果,您必须以一定的顺序使用三个API

1. shutdown
2. awaitTermination
3. shutdownNow
oracle文档页面的推荐方式,共页:

shutdown():
启动有序关机,其中previou