• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Ron McLeod
  • Jeanne Boyarsky
Sheriffs:
  • Paul Clapham
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
Bartenders:

Question 19 of Chapter 13 in book Java OCP 21 Certified Professional Study Guide

 
Greenhorn
Posts: 9
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Chapter 13 of "Java OCP 21 Certified Professional Study Guide"
The answer of Question 19 is "E, G" in the book, but I think G is not a good answer.

19. What is the result of executing the following application? (Choose all that apply.)


A. It compiles and outputs the two numbers followed by Printed.
B. The code will not compile because of line b1.
C. The code will not compile because of line b2.
D. The code will not compile because of line b3.
E. It compiles, but the output cannot be determined ahead of time.
F. It compiles but throws an exception at runtime.
G. It compiles but waits forever at runtime.

Is G really a good answer?  The instance of ExecutorService is created by calling method newVirtualThreadPerTaskExecutor,
even the executor is not terminated, but the code should still exit without hanging.
 
Marshal
Posts: 80956
522
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes, G is a good answer; you should always consider the possibility that a program will deadlock or livelock (e.g. go into an infinite loop). That does not happen here, at least I think it doesn't happen, so I think you are right that answeris incorrect. Maybe somebody else will explain why I am mistaken here and say that G is correct.
E is correct and A incorrect; because System.out.println() takes longer to execute than it does to start a new Thread, virtual or real, line 9 can be executed before any of the System.out.println() statements in line 8. That is indeed what happened to me.

jshell> PrintConstants.main()
27.1828
31.4159
Printed

jshell> PrintConstants.main()
31.4159
Printed
27.1828

jshell> PrintConstants.main()
Printed
31.4159
27.1828

When I added the Golden Ratio (= 0.5 * (1.0 + Math.sqrt(5.0))) as a third constant, I got the three constants printed in different orders:

jshell> PrintConstants.main()
31.41592653589793
16.18033988749895
27.18281828459045
Printed

jshell> PrintConstants.main()
31.41592653589793
27.18281828459045
16.18033988749895
Printed

jshell> PrintConstants.main()
16.18033988749895
31.41592653589793
27.18281828459045
Printed

jshell> PrintConstants.main()
31.41592653589793
27.18281828459045
Printed
16.18033988749895

jshell> PrintConstants.main()
31.41592653589793
27.18281828459045
Printed
16.18033988749895  [etc.]

 
Bartender
Posts: 5666
214
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is a very strange one! When I run the code many times, I get different results (using NetBeans 25 and java 24):

1) everything is printed and the code finishes (very occasionaly)
2) nothing is printed and the code keeps running (also very occasionaly)
3) nothing is printed and the code finishes (most of the time)
4) only "printed" is printed and the code finishes (often)
5) only one number is printed and the code finishes (sometimes)
6) only one number and "printed" are printed. code finishes (more than occasionaly, but less than sometimes)

I have been breaking my too little brain about this. The api says that a virtual thread is put away from the thread when there is a blocking I/O, and reappointed to some thread when the I/O is finished. So, if there is no output, then apparently there is no result from the println, and so the virtualthread remains blocked. And when it does print, then apparently the result from the println is noticed in some way. I do not understand what exactly is happening.
 
Rancher
Posts: 5193
84
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
We see the different outputs occur in somewhat random order.  That's fairly normal for multithreaded code.  The thing that's relatively new, to me, is that many events do not seem to occur at all.  Sometimes no events are printed; sometimes just one; sometimes two or three.  Why would that be?

I believe the key to understanding this one is to realize that virtual threads are always daemon threads.  This means that they will not prevent JVM shutdown.  Which means that, if the main() method gets to the end, there are no other non-daemon threads running, and the JVM can simply exit, without waiting for those daemon threads to complete.  In this case, that means that all the print statements may not appear, because if the main method gets to the end before a particular virtual thread runs, then that virtual thread will never run, because the JVM shuts down at that point. And it's entirely possible for the main method to run completely before any of the virtual threads has run.

To see this, you can add a Thread.sleep(1000) at the end (adding "throws InterruptedException" to the method declaration), and you are virtually guaranteed that you will see all three print statements appear (in some order).  In principle that's not strictly guaranteed, because we can't say how long those tasks might take to execute.  But in practice, 1000 milliseconds is far more time than needed, and all events will occur before the method exits.

Now having said that, I still don't see any reason why answer G would ever be true - I don't see any possibility of deadlock or livelock.  Does the book give any more explanation of that answer?
 
Piet Souris
Bartender
Posts: 5666
214
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Brilliant, Mike! Cow earned.

To test this, I added this to the end of the code (where r = new Random())

In about 50% of the runs nothing is printed, and the other 50% are split over the several possible outcomes. A run where the code keeps running did not occur in all my tries.
 
Campbell Ritchie
Marshal
Posts: 80956
522
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Oddly enough ,when I tried it on JShell, I got ten output lines at every attempt.
 
YuYu LiLi
Greenhorn
Posts: 9
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The original answer from the book for this question is:
E, G. The application compiles and does not throw an exception. Even though the stream is processed in sequential order, the tasks are submitted to a thread executor, which may complete the tasks in any order. Therefore, the output cannot be determined ahead of time, and option E is correct. Finally, the thread executor is never shut down; therefore, the code will run but never terminate, making option G also correct.
 
Mike Simmons
Rancher
Posts: 5193
84
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks for posting the book's explanation,  YuYu!  (Or LiLi if that's more appropriate.) Based on their explanation, I think they are simply wrong about answer G.  That explanation would be correct for many other types of executors, because most will create at least one other non-daemon thread, and keep running that/those thread(s) until shutdown() is called.  However, it doesn't seem to be true for newVirtualThreadPerTaskExecutor().  I suppose we might say that it's technically unspecified by the API, since the API never really discusses what happens if you don't call shutdown() and awaitTermination()... so we might imagine that it's theoretically possible that some implementation of this executor also spawns at least one non-daemon thread.  But that isn't how the existing implementation actually behaves.

The thing that we as programmers should take from this is that, if you don't call shutdown() and awaitTermination(), BAD THINGS can happen.  So don't do that.  Unfortunately, if someone asks exactly what bad things happen, then those details get fuzzy.  The API doesn't generally tell us precisely, but we can find out with some testing.

Incidentally, I tried this ExecutorService, and got similar results to the virtual thread executor:

Here, the new Threads are not daemon threads.  But, they create a new one for each task, meaning they don't need to keep the old ones alive for any reason.  So with this executor, there isn't necessarily any non-daemon thread alive at any particular time, even if there are still submitted tasks that have not run yet - and if the main method exits, the whole JVM can exit too, if there are no non-daemon threads.
 
YuYu LiLi
Greenhorn
Posts: 9
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you @Mike Simmons, I totally agree with you. I also did tests which were similar as yours. I post the question here because I want the authors of the book pay attention to this question and it's answers in the book. I didn't really find a good way to contact them.
 
Mike Simmons
Rancher
Posts: 5193
84
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is a good way to contact the authors - Jeanne Boyarsky checks in here periodically and catches up on posts like this.  So there's a good chance you'll see a response within a week or so.  Thanks for bringing the issue up here!
 
Campbell Ritchie
Marshal
Posts: 80956
522
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mike Simmons wrote:Thanks for posting the book's explanation, . . .

Yes, thank you
 
Campbell Ritchie
Marshal
Posts: 80956
522
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here is the documentation for ExecutorService#shutdown() and ExecutorService#awaitTermination(). No, it doesn't say what happens if the user doesn't call one or the other of those methods. But aren't virtual threads implicitly daemons? In which case won't they all terminate when their task is complete? Unless somebody does something like writing an infinite loop, that is, and we didn't see any of them. Readers should assume that code not shown in the question runs correctly.
 
Mike Simmons
Rancher
Posts: 5193
84
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Well, this was my main point, that in fact the virtual thread executor service can exit an any time, if there's no awaitTermination().  I was trying to find some justification for, at most[i], saying that the behavior was technically unspecified, to sort of justify the book's answer.  And it's a [i]really thin justification.  I think it's more likely they just assumed that the service would behave like many other executor services and keep running indefinitely if not closed.  And that's not the case.

To elaborate a little bit...  the only justification I can see is that the API for newVirtualThreadPerTaskExecutor() never quite guarantees that the new virtual threads for each of the tasks are the only threads that it creates.  (None of the other new???Executor() methods guarantee this either, as far as I see.). One can certainly say that there's no reason for other threads to be created - and, in fact, they aren't.  But I could imagine that someone might create an ExecutorService that uses one or more additional threads to, say, monitor for deadlocks maybe?  I'm not aware that any such implementations exist.  But it seems like they theoretically could.  

(We are told that newVirtualThreadPerTaskExecutor() is equivalent to newThreadPerTaskExecutor() with a factory that returns virtual threads.  So, creating other threads is permissible only if newThreadPerTaskExecutor() is allowed to create additional threads without using the provided factory.  And that seems pretty questionabletoo.  As I said, it's a very thin justification.)

Now in comparison, what the book says is: "Finally, the thread executor is never shut down; therefore, the code will run but never terminate". And that's just not true - except maybe in  some alternate universe, or a possible future with a different implementation than the current implementation.  If they had said "we can't know for sure if it will terminate or not", that's a bit more defensible.  But only a bit.
 
Greenhorn
Posts: 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Start with core Java → Learn OOP Practice with small projects Move to Spring Framework Explore tools like Maven Git, and JUnit.
 
Campbell Ritchie
Marshal
Posts: 80956
522
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
WG, welcome to the Ranch
 
Greenhorn
Posts: 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Answer: It prints null 10 times.
 
Campbell Ritchie
Marshal
Posts: 80956
522
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Welcome to the Ranch

Willam stock wrote:Answer: It prints null 10 times.

I haven't seen null printed at all.
 
That which doesn't kill us makes us stronger. I think a piece of pie wouldn't kill me. Tiny ad:
Clean our rivers and oceans from home
https://www.kickstarter.com/projects/paulwheaton/willow-feeders
reply
    Bookmark Topic Watch Topic
  • New Topic