如何在parallelStream正在执行时等待?

时间:2017-01-13 09:00:11

标签: java parallel-processing java-stream

所以,现在让我们交易吧。我有Parser类,我可以从html获取链接。我已经使用Jsoup获取链接,然后我想将此链接添加到List。我正在使用StreamAPI来继续。 请查看以下代码片段:

public static List<Link> getLinksFromURL(String URL) {
    List<Link> links = new ArrayList<>();
    Elements elements = selectElements(URL, "a[href]");
    elements.parallelStream().forEach((Parser) -> addLink(links, elements.attr("href")));
    return links;
}

有效。但我在处理什么?当我测试这个方法时,我注意到流执行可能会持续一段时间。我可以在我的测试类中得到错误的数据。例如。我检查列表大小。在调试期间一切都很好。但是在运行时我得到的元素很少而不是所有链接,因为尚未执行流。 简单的测试。但有时候size()== 3或更少:

@Test
public void getLinksFromURLTest() {
    Assert.assertTrue(Parser.getLinksFromURL("testLinks").size()==4);
}

我的问题是我在流执行时如何等待?我需要获得所有链接:)

小注意:我从我为测试启动的Spark服务器上的本地html中获取了html。

P.S。:如果我的书面不易理解,请告诉我,我会补充解释。

如果你帮助我,我会非常感激。 祝大家好运! :)

更新: addLink方法

private static void addLink(List<Link> links, String URL) {
    if (!URL.isEmpty() && isLink(URL) && !hasSameLink(links, URL)){
        links.add(new Link(URL));
    }
}

2 个答案:

答案 0 :(得分:3)

您在forEach()电话中犯了错误。对于每个元素,您在Elements.attr()变量上调用elements而不是当前项目的Element.attr()。你可能想做这样的事情:

forEach(element -> addLink(links, element.attr("href")))

无论如何,您的代码不是线程安全的。如果没有某种形式的同步(这很可能会破坏并行化的好处),您就无法将多个线程写入常规ArrayList。这也是您在测试中看到不一致结果的可能原因。您应该使用线程安全集合,或者只使用顺序迭代的棒。

或者,您可以将所有逻辑转换为流管道并改为使用收集器:

return elements.parallelStream()
        .filter(url -> !url.isEmpty())
        .filter(url -> isLink(url))
        .distinct()
        .map(Link::new)
        .collect(Collectors.toList());

答案 1 :(得分:1)

根据doc,您需要同步代码。您可以使用元素数初始化CountdownLatch,然后在主线程中等待它。