Stream.parallel()是否使用新线程?

时间:2016-03-20 11:43:23

标签: java multithreading java-8 java-stream

所以,我试图了解Java 8中引入的Stream API。我正在尝试创建一个可以在单独的线程上运行的流(仅用于教育目的)

String oracle = "http://www.oracle.com";
URL url = new URL(oracle);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
in.lines().parallel().forEach(System.out::println);
System.out.print("CLOSING THE INPUT STREAM!, shouldnt this crash?");
in.close();

结果不是我所期望的......(我原本期待崩溃,因为我关闭了输入流,而另一个线程正在读取它)。请注意.parallel()方法调用。相反,代码似乎是以顺序方式执行而没有任何问题。

输出:

<script language="JavaScript" src="http://www.oracleimg.com/us/assets/metrics/ora_ocom_hp.js"></script>
<!-- End SiteCatalyst code --> 

            <!-- SS_END_SNIPPET(fragment6,1)-->
<!-- SS_BEGIN_SNIPPET(fragment7,ui)-->          <!-- SS_END_SNIPPET(fragment7,ui)-->
</html>
CLOSING THE INPUT STREAM!, shouldnt this crash?

有谁知道发生了什么事?为什么我的代码崩溃?

3 个答案:

答案 0 :(得分:13)

并行流确实会尝试将读取行的工作分成多个线程。但是调用本身就是阻塞,即语句等待所有线程完成以继续下一个语句(关闭输入流)。

需要注意的一点是,forEach并不能保证并行操作的执行顺序与流元素的顺序相同,因此这种情况下的打印行与原始网页的顺序可能不同(见https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#forEach-java.util.function.Consumer-)。

答案 1 :(得分:2)

如果您想在后台执行操作而不立即阻止其完成,您可以使用java.util.concurrent.CompletableFuture.runAsync(Runnable)及相关方法。它返回一个CompletableFuture,如果需要,可以在以后加入。

答案 2 :(得分:1)

如前所述,并行流阻塞当前线程,直到所有并行任务完成。事实上,当前线程通常也用于执行某些工作,但如果它完成了它的部分,那么它会等待其他线程(或窃取他们的一些工作来帮助它们)。

但有一个特例:如果并行流操作抛出异常,那么主线程中的流处理完成(异常),但其他后台线程仍可继续处理某些输入块。您可以使用以下代码进行检查:

// Create list of Strings "0", "1", "2", ..., "99"
List<String> list = IntStream.range(0, 100).mapToObj(String::valueOf)
                             .collect(Collectors.toCollection(ArrayList::new));
// replace one with non-numeric
list.set(1, "foo");

// Convert every string to number and print it
try {
    list.parallelStream().mapToInt(Integer::parseInt).forEach(System.out::println);
} catch (NumberFormatException e) {
    // well some non-number encountered
}
System.out.println("Exited");

运行此代码时,您可能偶尔会看到"Exited"消息后打印了一些数字。