所以,我试图了解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?
有谁知道发生了什么事?为什么我的代码不崩溃?
答案 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"
消息后打印了一些数字。