繁体   English   中英

Java线程-等待所有子线程以继续

[英]Java threads - waiting on all child threads in order to proceed

有一点背景知识;

我正在一个项目中,该servlet将在文件系统中的许多文本文件上释放搜寻器。 我正在考虑将负载划分为多个线程,例如:

搜寻器进入目录,找到3个文件和6个目录。 它将开始处理文件,并使用其他目录的新搜寻器启动线程。 因此,从我的创建者类中,我将在基本目录上创建一个搜寻器。 搜寻器会评估工作负载,如果认为需要,它将在另一个线程下生成另一个搜寻器。

我的爬虫类看起来像这样

package com.fujitsu.spider;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;

public class DocumentSpider implements Runnable, Serializable {

private static final long serialVersionUID = 8401649393078703808L;
private Spidermode currentMode = null;
private String URL = null;
private String[] terms = null;
private float score = 0;

private ArrayList<SpiderDataPair> resultList = null;

public enum Spidermode {
    FILE, DIRECTORY
}

public DocumentSpider(String resourceURL, Spidermode mode, ArrayList<SpiderDataPair> resultList) {
    currentMode = mode;
    setURL(resourceURL);
    this.setResultList(resultList);
}

@Override
public void run() {
    try {
        if (currentMode == Spidermode.FILE) {
            doCrawlFile();
        } else {
            doCrawlDirectory();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    System.out.println("SPIDER @ " + URL + " HAS FINISHED.");
}

public Spidermode getCurrentMode() {
    return currentMode;
}

public void setCurrentMode(Spidermode currentMode) {
    this.currentMode = currentMode;
}

public String getURL() {
    return URL;
}

public void setURL(String uRL) {
    URL = uRL;
}

public void doCrawlFile() throws Exception {
    File target = new File(URL);

    if (target.isDirectory()) {
        throw new Exception(
                "This URL points to a directory while the spider is in FILE mode. Please change this spider to FILE mode.");
    }

    procesFile(target);
}

public void doCrawlDirectory() throws Exception {
    File baseDir = new File(URL);

    if (!baseDir.isDirectory()) {
        throw new Exception(
                "This URL points to a FILE while the spider is in DIRECTORY mode. Please change this spider to DIRECTORY mode.");
    }

    File[] directoryContent = baseDir.listFiles();

    for (File f : directoryContent) {
        if (f.isDirectory()) {
            DocumentSpider spider = new DocumentSpider(f.getPath(), Spidermode.DIRECTORY, this.resultList);
            spider.terms = this.terms;
            (new Thread(spider)).start();
        } else {
            DocumentSpider spider = new DocumentSpider(f.getPath(),      Spidermode.FILE, this.resultList);
            spider.terms = this.terms;
            (new Thread(spider)).start();
        }
    }
}

public void procesDirectory(String target) throws IOException {
    File base = new File(target);
    File[] directoryContent = base.listFiles();

    for (File f : directoryContent) {
        if (f.isDirectory()) {
            procesDirectory(f.getPath());
        } else {
            procesFile(f);
        }
    }
}

public void procesFile(File target) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(target));
    String line;
    while ((line = br.readLine()) != null) {

        String[] words = line.split(" ");
        for (String currentWord : words) {
            for (String a : terms) {
                if (a.toLowerCase().equalsIgnoreCase(currentWord)) {
                    score += 1f;
                }
                ;
                if (currentWord.toLowerCase().contains(a)) {
                    score += 1f;
                }
                ;
            }
        }
    }

    br.close();
    resultList.add(new SpiderDataPair(this, URL));
}

public String[] getTerms() {
    return terms;
}

public void setTerms(String[] terms) {
    this.terms = terms;
}

public float getScore() {
    return score;
}

public void setScore(float score) {
    this.score = score;
}

public ArrayList<SpiderDataPair> getResultList() {
    return resultList;
}

public void setResultList(ArrayList<SpiderDataPair> resultList) {
    this.resultList = resultList;
}

}

我面临的问题是,在我的根搜寻器中,我具有要进一步处理的每个搜寻器的结果列表。 从servlet(或本示例的main方法)调用处理此列表中的数据的操作。 但是,总是在所有搜寻器完成其处理之前调用该操作。 因此过早启动了处理结果的操作,这导致数据不完整。

我尝试使用join方法解决此问题,但不幸的是我似乎无法弄清楚这一点。

package com.fujitsu.spider;

import java.util.ArrayList;

import com.fujitsu.spider.DocumentSpider.Spidermode;

public class Main {

public static void main(String[] args) throws InterruptedException {
    ArrayList<SpiderDataPair> results = new ArrayList<SpiderDataPair>();
    String [] terms = {"SERVER","CHANGE","MO"};

    DocumentSpider spider1 = new DocumentSpider("C:\\Users\\Mark\\workspace\\Spider\\Files", Spidermode.DIRECTORY, results);
    spider1.setTerms(terms);

    DocumentSpider spider2 = new DocumentSpider("C:\\Users\\Mark\\workspace\\Spider\\File2", Spidermode.DIRECTORY, results);
    spider2.setTerms(terms);

    Thread t1 = new Thread(spider1);
    Thread t2 = new Thread(spider2);


    t1.start();
    t1.join();

    t2.start();
    t2.join();

    for(SpiderDataPair d : spider1.getResultList()){
        System.out.println("PATH -> " + d.getFile() + " SCORE -> " + d.getSpider().getScore());
    }

    for(SpiderDataPair d : spider2.getResultList()){
        System.out.println("PATH -> " + d.getFile() + " SCORE -> " + d.getSpider().getScore());
    }

}

}

TL:DR 在此处输入图片说明

我真的很想了解这个主题,因此我们将不胜感激!

您需要在代码中进行一些更改:

在蜘蛛中:

List<Thread> threads = new LinkedList<Thread>();
for (File f : directoryContent) {
    if (f.isDirectory()) {
        DocumentSpider spider = new DocumentSpider(f.getPath(), Spidermode.DIRECTORY, this.resultList);
        spider.terms = this.terms;
        Thread thread = new Thread(spider);
        threads.add(thread)
        thread.start();
    } else {
        DocumentSpider spider = new DocumentSpider(f.getPath(),      Spidermode.FILE, this.resultList);
        spider.terms = this.terms;
        Thread thread = new Thread(spider);
        threads.add(thread)
        thread.start();
    }
}
for (Thread thread: threads) thread.join()

这个想法是为每个蜘蛛创建一个新线程并启动它。 它们全部运行之后,您要等到每次完成操作之后,Spider本身才能完成。 这样,每个蜘蛛线程将一直运行,直到完成所有工作为止(因此,顶线程将一直运行,直到所有子代及其子代都完成)。

您还需要更改跑步者,以使其并行运行两个蜘蛛,而不是像这样一个接一个地运行:

Thread t1 = new Thread(spider1);
Thread t2 = new Thread(spider2);
t1.start();
t2.start();
t1.join();
t2.join();

对于此任务,您应该使用比裸Thread更高级别的库。 我建议特别研究ExecutorService以及所有java.util.concurrent 那里有抽象,可以管理所有线程问题,同时为格式良好的任务提供运行时受到适当保护的环境。

对于您的特定问题,我建议使用某种阻塞任务队列和标准的生产者-消费者体系结构。 每个任务都知道如何确定其路径是文件还是目录。 如果是文件,则处理该文件; 如果是目录,请爬网目录的立即内容,并为每个子路径排队新任务。 您还可以使用一些正确同步的共享状态来限制已处理文件的数量,深度等。此外,该服务还提供了等待其任务终止的功能,从而使“加入”变得更加简单。

使用这种体系结构,您可以将线程和线程管理(由ExecutorService处理)的概念与任务(通常是RunnableCallable )的业务逻辑分离。 服务本身具有调整实例化能力的能力,例如,根据存在的并发任务数,固定的最大线程数或可伸缩的数目(请参见java.util.concurrent.Executors上的工厂方法)。 比其执行的Runnable更为昂贵的Thread被重新使用以节省资源。

如果您的目标主要是可以提高生产质量的功能,那么图书馆就是您的理想之选。 但是,如果您的目标是了解线程管理的较低级别的详细信息,那么您可能希望研究闩锁的使用,也许还需要研究线程组在较低级别上的管理,从而公开实现的详细信息,以便您可以使用细节。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM