【发布时间】:2021-08-05 08:57:22
【问题描述】:
我有一个在 Executor.newCachedThreadPool() 的帮助下工作的解析器,并面临这样一个事实,即写入 JSON 文件的记录的主线程在子线程之前执行。结果,我们有一个空文件... 我对多线程的主题知之甚少,无法理解错误。我尝试在主线程上使用join()方法,但最后程序刚到这部分就挂了
Main.java
import model.Product;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Main {
public static void main(String[] args) throws InterruptedException {
String rootUrl = "example.com";
System.out.println("Started parsing: " + rootUrl);
long m = System.currentTimeMillis();
HtmlParser htmlParser = new HtmlParser();
List<Product> productList = new CopyOnWriteArrayList<>();
htmlParser.parse(rootUrl, productList);
Printer.printToJson(productList);
System.out.println("Finish: completed in " + ((double) System.currentTimeMillis() - m) / 1000 + " seconds");
}
}
HtmlParser.java
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import ua.bala.model.Product;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class HtmlParser {
private static AtomicInteger httpRequestsCounter = new AtomicInteger(0);
public static AtomicInteger getHttpRequestsCounter() {
return httpRequestsCounter;
}
public void parse(String url, List<Product> productList) {
try {
Document page = getPage(url);
parsePage(page, productList);
} catch (IOException e) {
e.printStackTrace();
}
}
private static Document getPage(String url) throws IOException {
Document document = Jsoup.connect(url).get();
httpRequestsCounter.getAndIncrement();
return document;
}
private void parsePage(Document page, List<Product> productList) {
Elements productElements = page.select("a.dgBQdu");
ExecutorService service = Executors.newCachedThreadPool();
for (Element element: productElements){
service.execute(() -> {
Long articleID = Long.parseLong(element.attr("id"));
String name = "NAME";
String brand = "BRAND";
BigDecimal price = new BigDecimal(BigInteger.ZERO);
Set<String> colors = new HashSet<>();
String url = "https://www.aboutyou.de" + element.attr("href");
Document innerPage;
try {
innerPage = getPage(url);
Element innerElement = innerPage.selectFirst("[data-test-id='BuyBox']");
name = innerElement.selectFirst("div.dZjUXd").text();
brand = innerElement.selectFirst("[data-test-id='BrandLogo']").attr("alt");
colors = new HashSet<>(innerElement.select("span.jlvxcb-1").eachText());
String priceStr = innerElement.selectFirst("div.dWWxvw > span").text().replace("ab ","").replace(" EUR","").replace(",", ".");
price = new BigDecimal(priceStr);
} catch (IOException e) {
e.printStackTrace();
}
Product product = new Product(articleID, name, brand, colors, price, url);
addProduct(product, productList);
});
}
service.shutdown();
}
private synchronized void addProduct(Product product, List<Product> productList){
System.out.println("Product " + product.getID() + " parsed");
System.out.print(product);
productList.add(product);
System.out.printf("Product %d added to list\n%n", product.getID());
}
}
Printer.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import model.Product;
import java.io.*;
import java.util.Comparator;
import java.util.List;
public class Printer {
private static final String path = "";
private static final String fileName = "productsOutput";
public static void printToJson(List<Product> products){
products.sort(Comparator.comparing(Product::getID));
System.out.println("Product list start printing to JSON");
try (final Writer writer = new FileWriter(path + fileName + ".json")) {
Gson gson = new GsonBuilder().create();
gson.toJson(products, writer);
System.out.println("Product list printed to JSON");
System.out.printf("Amount of triggered HTTP requests: %s%nAmount of extracted products: %s%n",
HtmlParser.getHttpRequestsCounter(), products.size());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Printer.java
package model;
import lombok.*;
import java.math.BigDecimal;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
@NoArgsConstructor
@Getter
@Setter
public class Product {
private static AtomicLong productsCounter = new AtomicLong(1);
private Long ID;
private Long articleID;
private String name;
private String brand;
private BigDecimal price;
private Set<String> colors;
private String url;
{
ID = productsCounter.getAndIncrement();
}
public Product(Long articleID, String name, String brand, Set<String> colors, BigDecimal price, String url) {
this.articleID = articleID;
this.name = name;
this.brand = brand;
this.price = price;
this.colors = colors;
this.url = url;
}
public static AtomicLong getProductsCounter() {
return productsCounter;
}
@Override
public String toString() {
return String.format("%d\t%d\t%s\t%s\t%s\t%s\t%s\n", ID, articleID, name, brand, price, colors, url);
}
}
【问题讨论】:
-
'结果......':不,不是。在所有非守护线程都退出之前,JVM 不会退出。您可能有一个空文件,但不是出于这个原因。这是因为您在计算完成之前打印结果。在计算线程中打印它,而不是在
main()。
标签: java multithreading executorservice