Я создаю веб-скребок для извлечения ссылок и электронных писем из Интернета. Ссылки будут использоваться для поиска новых мест для поиска электронных писем, а затем электронные письма будут храниться в наборе. Каждая ссылка передается в фиксированный пул потоков в своем собственном потоке для поиска дополнительных электронных писем. Я начал с малого и искал только 10 писем, но по какой-то причине мой код возвращает около 13 писем.
while (emailSet.size() <= EMAIL_MAX_COUNT) {
link = linksToVisit.poll();
linksToVisit.remove(link);
linksVisited.add(link);
pool.execute(new Scraper(link));
}
pool.shutdownNow();
emailSet.stream().forEach((s) -> {
System.out.println(s);
});
System.out.println(emailSet.size());
Хотя я понимаю, что можно создать дополнительные темы, которые будут работать после того, как я получу 10 электронных писем, не следует ли pool.shutdownNow()
завершать эти темы?
Вот мой код темы, если это поможет.
class Scraper implements Runnable {
private String link;
Scraper(String s) {
link = s;
}
@Override
public void run() {
try {
Document doc = (Document) Jsoup.connect(link).get();
Elements links = doc.select("a[href]");
for (Element href : links) {
String newLink = href.attr("abs:href");
if (!linksVisited.contains(newLink) && !linksToVisit.contains(newLink)) {
linksToVisit.add(newLink);
}
}
Pattern p = Pattern.compile(
"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+");
Matcher matcher = p.matcher(doc.text());
while (matcher.find()) {
emailSet.add(matcher.group());
}
} catch (Exception e) {
//Catch on of the many exceptions Jsoup.connect might throw
// and just let the thread expire.
}
}
}
Редактировать 1:
Я должен включить это в свой первый раз, но я использую потокобезопасный набор и очередь.
Set<String> emailSet = Collections.synchronizedSet(new HashSet());
BlockingQueue<String> linksToVisit = new ArrayBlockingQueue(10000);
Set<String> linksVisited = Collections.synchronizedSet(new HashSet());
final int EMAIL_MAX_COUNT = 10;
ExecutorService pool = newFixedThreadPool(25);
Редактировать 2
Решил, что я должен обновить свой вопрос с ответом, так что вот где была моя проблема.
while (emailSet.size() <= EMAIL_MAX_COUNT) {
link = linksToVisit.poll();
linksToVisit.remove(link);
linksVisited.add(link);
pool.execute(new Scraper(link));
}
Мой список начнется только с одной ссылки. После того, как первая ссылка была удалена, у меня был пустой список, который продолжал создавать новые темы без ссылок для поиска. Прежде чем список мог быть заполнен, я уже создал сотни потоков, которые ничего не делали, но замедляли работу системы, пока она, наконец, не рухнула.
Вот исправление кода, чтобы гарантировать, что потоки не будут созданы, если не будет ссылки для поиска.
while (emailSet.size() <= EMAIL_MAX_COUNT) {
if (linksToVisit.size() > 0) {
link = linksToVisit.poll();
linksToVisit.remove(link);
linksVisited.add(link);
pool.execute(new Scraper(link));
//System.out.println("Emails " + emailSet.size());
} else {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Crawler.class.getName())
.log(Level.SEVERE, null, ex);
}
}
}