5

So, I'm trying to get my head around the Stream API introduced in Java 8. I'm trying to make a stream that can run on a separate thread (just for educational purposes)

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(); 

The outcome is not what I would expect.. (I was expecting a crash, since I closed the input stream while the other thread is reading from it). Note the .parallel() method call. Instead the code seems to be executing in a sequential manner with no problems.

OUTPUT:

<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? 

Does anyone know what's going on? Why is my code not crashing?

1
  • 2
    You can use ForkJoinPool if you don't want things to be blocked. Commented Mar 20, 2016 at 11:49

3 Answers 3

13

The parallel stream will indeed attempt splitting the work of reading the lines to mutiple threads. But the invocation itself is blocking, i.e. the statement waits until all threads finished to proceed to the next statement (where you close the input stream).

One thing to note is that forEach does not guarantee that the parallel actions execute in the same order of the stream elements, so the printed lines in this case may not be in the same order with the original Web page (see https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#forEach-java.util.function.Consumer-).

Sign up to request clarification or add additional context in comments.

5 Comments

I understand, good answer, what's the point of creating a new thread if you are going to leave the current one idle though?. .parallel() doenst seem to be all that useful. I guess that's a different question. Will accept your question as accepted as soon as I can. Thanks
@feresr: The point is that, if it spawns (say) ten threads to do the work in parallel, then the whole operation might go ten times faster than if carried out in just one. (In practice it may not go that fast - or may go even faster, due to caching effects - but that's the idea, anyway!)
With parallel streams, the execution may be faster. You can try measuring the difference against a sequential stream. It still needs to block because forEach is a terminal operation which needs to combine a certain result. You could also call a sum operation in which case it must wait for the result to be combined.
@feresr This article gives a good example.
I see, my confusion came from thinking .parallel() worked a little bit as the methods .subscribeOn and .observeOn from RxJava. I understand that's not the case. thank guys, it makes more sense now.
2

If you want to execute things in the background without immediately blocking for their completion you can use java.util.concurrent.CompletableFuture.runAsync(Runnable) and related methods. It returns a CompletableFuture which can be joined later if needed.

Comments

2

As it was already noted, parallel stream blocks the current thread until all the parallel tasks are finished. In fact current thread is usually also used to perform some work, but if it finishes its part, then it waits for other threads (or steals some of their work to help them).

There's one special case though: if the parallel stream operation throws an Exception, then your stream processing in the main thread finishes (exceptionally), but other background threads may still continue processing some input chunks. You can check this using the following code:

// 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"); 

Running this code you may occasionally see that some numbers are printed after the "Exited" message.

1 Comment

Is this still true? I am trying this over and over in Java 24-ea, and I can't get it to do the same as you did. Which is great, good riddance. But I want to make sure it is definitively gone. Do you know?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.