Diagnostic Quiz
Problems
1. Tricky "Thread Execution" Sequence
System.out.printlnis a synchronous method call!
So, the following code will guarantee that hello is printed before world.
System.out.println("hello");
new Thread(() -> { System.out.println("world"); }).start();2. Possible Thread Execution Sequence
In Thread programming, there is no sequence of which thread will be executed first. So, be always careful, there may be lots of possibilities!

For this question, actually, we are not guaranteed that * will be printed first, it's just because we need to choose all the possible output from these five options (these five options are not exhaustive)!
*3-5. When is CompletableFuture<T> complete?
CompletableFuture<T> complete?
If it is a
CompletableFuture<T>, let's sayCompleteableFuture<...> cf;
If
cf = CompletableFuture.supplyAsync(s), then after the method calls.get(),cfis completeIf
cf = CompletableFuture.runAsync(r)is a Runnable, then after the method callr.run(),cfis completeThis applies no matter how many nested CompletableFuture are in
sorr, as long as finallysandrare Supplier or Runnable.If it is a
CompletedFuture, let's sayCompleteableFuture<...> cf = CompletableFuture.completedFuture(f), wherefis a normal method.cfis complete immediately after the creating statementCompletableFuture::completedFuture.

According to our rule of thumb, cf is complete only after the method call of s2.get().
*8. Possible output with no join()
join()If no
.join()or.get()is called after the CompletableFuture, your output may have some possibilities, a.k.a non-determinstic output.

Since no .join() or .get() is called, we have three possible correct options!
*10. Possible output with anyOf()
anyOf()Possible output with
anyOf()is a bit tricky, fully utilize your exhausitive thinking.

In this question, the printing of 1 and 2 are actually very flexible, as long as there is at least one 1 or 2 in front of 3, that will be enough. So, exhaustively, there will be 6 possibilities, 123, 213, 132, 231, 13, 23.
11-15. ForkJoinPool
task.compute()is just a normal method call, this target task won't be added to the current worker's task dequeue.Use the rule of thumb from Lec 11! Include into cheatsheet.
When dealing with work stealing problem, always write down the content of the worker to-be-stolen's task dequeue, and its task at the tail will be stolen!
When we call new Task(1).compute(), it creates two subtasks, denoted as Task(2) and Task(3). Task(2) creates two more subtasks Task(4) and Task(5); while Task(3) creates two more subtasks Task(6) and Task(7).
11. One worker dequeue analysis
Suppose there is only one worker thread and it runs
After Task(3) is forked, what is the content of the deque for this worker thread? (The content is listed from head to tail, with the head first and the tail last).
Solution:
new Task(1).compute()is just a normal method call, so Task(1) won't be added to the worker's dequeue.Inside
Task(1).compute(), Task(2) is forked first, thus, it is added to the head of the worker's dequeue first. Then similar for Task (3)Thus, at the end of the day, the dequeue contains
[3 2].
12. Adding join() call to the analysis
join() call to the analysisAfter the join for Task(3) is invoked, what is the content of the deque for this worker thread? (The content is listed from head to tail, with the head first and the tail last).
Solution: Use the rule of thumb from lec, there is only one worker here. So, when .join() of Task(3) is invoked, it belongs to the first case, and Task(3) will pop out first and its compute() method will be called. Then, use the similar analysis from 11. One worker dequeue analysis, we have our dequeue to be [7 6 2].
13. Continue from 12
After the join for Task(2) is invoked, what is the content of the deque for this worker thread? (The content is listed from head to tail, with the head first and the tail last).
Now, Task(3) is finished, so our dequeue only contains Task(2). And then, use the similar analysis from 12. Adding join() call to the analysis, we have our dequeue to be [5 4].
14. Work Stealing
Suppose that there is a second worker thread who tries to steal from the first worker thread, while it is running Task(3). Which task would it steal?
Solution:
Find the content of the dequeue while running Task(3): It should be
[7 6 2]Use rule of thumb on work stealing: Task(2) will be stolen.
Tips
System.out.printlnis a synchronous method call!In Thread programming, there is no sequence of which thread will be executed first. So, be always careful, there may be lots of possibilities!
Rule of Thumb to detect when a completable future is complete
If it is a
CompletableFuture<T>, let's sayCompleteableFuture<...> cf;If
cf = CompletableFuture.supplyAsync(s), then after the method calls.get(),cfis completeIf
cf = CompletableFuture.runAsync(r)is a Runnable, then after the method callr.run(),cfis completeThis applies no matter how many nested CompletableFuture are in
sorr, as long as finallysandrare Supplier or Runnable.
If it is a
CompletedFuture, let's sayCompleteableFuture<...> cf = CompletableFuture.completedFuture(f), wherefis a normal method.cfis complete immediately after the creating statementCompletableFuture::completedFuture.
If no
.join()or.get()is called after the CompletableFuture, your output may have some possibilities, a.k.a non-determinstic output.Possible output with
anyOf()is a bit tricky, fully utilize your exhausitive thinking.task.compute()is just a normal method call, this target task won't be added to the current worker's task dequeue.Use the rule of thumb from Lec 11! Include into cheatsheet.
When dealing with work stealing problem, always write down the content of the worker to-be-stolen's task dequeue, and its task at the tail will be stolen!
Last updated