Programmer II Chapter 6: Concurrency Flashcards
What does this code output?
List favNumbers =
new CopyOnWriteArrayList < > (List.of(4,3,42));
for(var n: favNumbers) {
System.out.print(n + “ “);
favNumbers.add(9);
}
System.out.println();
System.out.println(“Size: “ + favNumbers.size());
4 3 42
Size: 6
Despite adding elements to the array while iterating over it, the for loop only iterated on the ones created when the loop started. Alternatively, if we had used a regular ArrayList object, a ConcurrentModificationException would have been thrown at runtime. With either class, though, we avoid entering an infinite loop in which elements are constantly added to the array as we iterate over them.
Given an instance of a Stream s and a Collection c, which are valid ways of creating a parallel stream? (Choose all that apply.)
A. new ParallelStream(s) B. c.parallel() C. s.parallelStream() D. c.parallelStream() E. new ParallelStream(c) F. s.parallel()
D, F. There is no such class within the Java API called ParallelStream, so options A and E are incorrect. The method defined in the Stream class to create a parallel stream from an existing stream is parallel(); therefore, option F is correct, and option C is incorrect. The method defined in the Collection class to create a parallel stream from a collection is parallelStream(); therefore, option D is correct, and option B is incorrect.
Given that the sum of the numbers from 1 (inclusive) to 10 (exclusive) is 45, what are the possible results of executing the following program? (Choose all that apply.)
import java.util.concurrent.locks.*; import java.util.stream.*;
public class Bank { private Lock vault = new ReentrantLock(); private int total = 0;
public void deposit(int value) { try { vault.tryLock(); total += value; } finally { vault.unlock(); } } public static void main(String[] unused) { var bank = new Bank(); IntStream.range(1, 10).parallel() .forEach(s -> bank.deposit(s)); System.out.println(bank.total); } }
A. 45 is printed. B. A number less than 45 is printed. C. A number greater than 45 is printed. D. An exception is thrown. E. None of the above, as the code does not compile
A, D. The tryLock() method returns immediately with a value of false if the lock cannot be acquired. Unlike lock(), it does not wait for a lock to become available. This code fails to check the return value, resulting in the protected code being entered regardless of whether the lock is obtained.In some executions (when tryLock() returns true on every call), the code will complete successfully and print 45 at runtime, making option A correct. On other executions (when tryLock() returns false at least once), the unlock() method will throw an IllegalMonitorStateException at runtime, making option D correct. Option B would be possible if there was no lock at all, although in this case, failure to acquire a lock results in an exception at runtime
Which of the following statements about the Callable call() and Runnable run() methods are correct? (Choose all that apply.)
A. Both can throw unchecked exceptions.
B. Callable takes a generic method argument.
C. Callable can throw a checked exception.
D. Both can be implemented with lambda expressions.
E. Runnable returns a generic type.
F. Callable returns a generic type.
G. Both methods return void.
A, C, D, F.
All methods are capable of throwing unchecked exceptions, so option A is correct. Runnable and Callable statements both do not take any arguments, so option B is incorrect. Only Callable is capable of throwing checked exceptions, so option C is also correct. Both Runnable and Callable are functional interfaces that can be implemented with a lambda expression, so option D is also correct. Finally, Runnable returns void and Callable returns a generic type, making option F correct and making options E and G incorrect.
Which lines need to be changed to make the code compile? (Choose all that apply.)
ExecutorService service = // w1 Executors.newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(() -> { System.out.println("Open Zoo"); return null; // w2 }, 0, 1, TimeUnit.MINUTES);
var result = service.submit(() -> // w3 System.out.println("Wake Staff"));
System.out.println(result.get()); // w4
A. It compiles and runs without issue. B. Line w1 C. Line w2 D. Line w3 E. Line w4 F. It compiles but throws an exception at runtime.G. None of the above
B, C.
The code does not compile, so options A and F are incorrect. The first problem is that although a ScheduledExecutorService is created, it is assigned to an ExecutorService. The type of the variable on line w1 would have to be updated to ScheduledExecutorService for the code to compile,making option B correct. The second problem is that scheduleWithFixedDelay() supports only Runnable, not Callable, and any attempt to return a value is invalid in a Runnable lambda expression; therefore, line w2 will also not compile, and option C is correct. The rest of the lines compile without issue, so options D and E are incorrect.
What statement about the following code is true?
var value1 = new AtomicLong(0); final long[] value2 = {0}; IntStream.iterate(1, i -> 1).limit(100).parallel() .forEach(i -> value1.incrementAndGet()); IntStream.iterate(1, i -> 1).limit(100).parallel() .forEach(i -> ++value2[0]); System.out.println(value1+" "+value2[0]);
A. It outputs 100 100.
B. It outputs 100 99.
C. The output cannot be determined ahead of time.
D. The code does not compile.
E. It compiles but throws an exception at runtime.
F. It compiles but enters an infinite loop at runtime.
G. None of the above
C.
The code compiles and runs without throwing an exception or entering an infinite loop, so options D, E, and F are incorrect. The key here is that the increment operator ++ is not atomic. While the first part of the output will always be 100, the second part is nondeterministic. It could output any value from 1 to 100, because the threads can overwrite each other’s work. Therefore, option C is the correct answer, and options A and B are incorrect.
Which statements about the following code are correct? (Choose all that apply.)
public static void main(String[] args) throws Exception { var data = List.of(2,5,1,9,8); data.stream().parallel() .mapToInt(s -> s) .peek(System.out::println) .forEachOrdered(System.out::println); }
A. The peek() method will print the entries in the order: 1 2 5 8 9.
B. The peek() method will print the entries in the order: 2 5 1 9 8.
C. The peek() method will print the entries in an order that cannot be determined ahead of time.
D. The forEachOrdered() method will print the entries in the order: 1 2 5 8 9.
E. The forEachOrdered() method will print the entries in the order: 2 5 1 9 8.
F. The forEachOrdered() method will print the entries in an order that cannot be determined ahead of time.
G. The code does not compile
C, E.
The code compiles, so option G is incorrect. The peek() method on a parallel stream will process the elements concurrently, so the order cannot be determined ahead of time, and option C is correct. The forEachOrdered() method will process the elements in the order they are stored in the stream, making option E correct. It does not sort the elements, so option D is incorrect.
Fill in the blanks:___________ occur(s) when two or more threads are blocked forever but both appear active.___________ occur(s) when two or more threads try to complete a related task at the same time, resulting in invalid or unexpected data.
A. Livelock, Deadlock B. Deadlock, Starvation C. Race conditions, Deadlock D. Livelock, Race conditions E. Starvation, Race conditions F. Deadlock, Livelock
D.
Livelock occurs when two or more threads are conceptually blocked forever, although they areeach still active and trying to complete their task. A race condition is an undesirable result that occurswhen two tasks are completed at the same time, which should have been completed sequentially.
Assuming this class is accessed by only a single thread at a time, what is the result of calling the countIceCreamFlavors() method?
import java.util.stream.LongStream; public class Flavors { private static int counter; public static void countIceCreamFlavors() { counter = 0; Runnable task = () -> counter++; LongStream.range(1, 500) .forEach(m -> new Thread(task).run()); System.out.println(counter); } }
A. The method consistently prints 499.
B. The method consistently prints 500.
C. The method compiles and prints a value, but that value cannot be determined ahead of time.
D. The method does not compile.
E. The method compiles but throws an exception at runtime.
F. None of the above
A.
The method looks like it executes a task concurrently, but it actually runs synchronously. In eachiteration of the forEach() loop, the process waits for the run() method to complete before movingon. For this reason, the code is actually thread-safe. It executes a total of 499 times, since the secondvalue of range() excludes the 500. Since the program consistently prints 499 at runtime, option A iscorrect. Note that if start() had been used instead of run() (or the stream was parallel), then theoutput would be indeterminate, and option C would have been correct.
Which happens when a new task is submitted to an ExecutorService, in which there are no threads available?
A. The executor throws an exception when the task is submitted.
B. The executor discards the task without completing it.
C. The executor adds the task to an internal queue and completes when there is an available thread.
D. The thread submitting the task waits on the submit call until a thread is available beforecontinuing.
E. The executor creates a new temporary thread to complete the task
C.
If a task is submitted to a thread executor, and the thread executor does not have any availablethreads, the call to the task will return immediately with the task being queued internally by the threadexecutor. For this reason, option C is the correct answer.
What is the result of executing the following code snippet?
List lions = new ArrayList < > (List.of(1,2,3)); List tigers = new CopyOnWriteArrayList < > (lions); Set < Integer > bears = new ConcurrentSkipListSet < > (); bears.addAll(lions); for(Integer item: tigers) tigers.add(4); // x1 for(Integer item: bears) bears.add(5); // x2 System.out.println(lions.size() + " " + tigers.size() \+ " " + bears.size());
A. It outputs 3 6 4.
B. It outputs 6 6 6.
C. It outputs 6 3 4.
D. The code does not compile.
E. It compiles but throws an exception at runtime on line x1.
F. It compiles but throws an exception at runtime on line x2.
G. It compiles but enters an infinite loop at runtime.
A. The code compiles without issue, so option D is incorrect. The CopyOnWriteArrrayList class is designed to preserve the original list on iteration, so the first loop will be executed exactly three timesand, in the process, will increase the size of tigers to six elements. The ConcurrentSkipListSetclass allows modifications, and since it enforces uniqueness of its elements, the value 5 is added onlyonce leading to a total of four elements in bears. Finally, despite using the elements of lions topopulate the collections, tigers and bears are not backed by the original list, so the size of lions is 3throughout this program. For these reasons, the program prints 3 6 4, and option A is correct.
What statements about the following code are true? (Choose all that apply.)
Integer i1 = List.of(1, 2, 3, 4, 5).stream().findAny().get(); synchronized(i1) { // y1 Integer i2 = List.of(6, 7, 8, 9, 10) .parallelStream() .sorted() .findAny().get(); // y2 System.out.println(i1 + " " + i2); }
A. The first value printed is always 1.
B. The second value printed is always 6.
C. The code will not compile because of line y1.
D. The code will not compile because of line y2.
E. The code compiles but throws an exception at runtime.
F. The output cannot be determined ahead of time.
G. It compiles but waits forever at runtime.
F.
The code compiles and runs without issue, so options C, D, E, and G are incorrect. There are twoimportant things to notice. First, synchronizing on the first variable doesn’t actually impact the resultsof the code. Second, sorting on a parallel stream does not mean that findAny() will return the firstrecord. The findAny() method will return the value from the first thread that retrieves a record.Therefore, the output is not guaranteed, and option F is correct. Option A looks correct, but even onserial streams, findAny() is free to select any element.
Assuming takeNap() is a method that takes five seconds to execute without throwing an exception,what is the expected result of executing the following code snippet?
ExecutorService service = null; try { service = Executors.newFixedThreadPool(4); service.execute(() -> takeNap()); service.execute(() -> takeNap()); service.execute(() -> takeNap()); } finally { if (service != null) service.shutdown(); } service.awaitTermination(2, TimeUnit.SECONDS); System.out.println("DONE!");
A. It will immediately print DONE!.
B. It will pause for 2 seconds and then print DONE!.
C. It will pause for 5 seconds and then print DONE!.
D. It will pause for 15 seconds and then print DONE!.
E. It will throw an exception at runtime.
F. None of the above, as the code does not compile
B.
The code snippet submits three tasks to an ExecutorService, shuts it down, and then waits for theresults. The awaitTermination() method waits a specified amount of time for all tasks to complete,and the service to finish shutting down. Since each five-second task is still executing, theawaitTermination() method will return with a value of false after two seconds but not throw anexception. For these reasons, option B is correct.
What statements about the following code are true? (Choose all that apply.)
System.out.print(List.of("duck","flamingo","pelican") .parallelStream().parallel() // q1 .reduce(0, (c1, c2) -> c1.length() + c2.length(), // q2 (s1, s2) -> s1 + s2)); // q3
A. It compiles and runs without issue, outputting the total length of all strings in the stream.
B. The code will not compile because of line q1.
C. The code will not compile because of line q2.
D. The code will not compile because of line q3.
E. It compiles but throws an exception at runtime.
F. None of the above
C.
The code does not compile, so options A and E are incorrect. The problem here is that c1 is an intand c2 is a String, so the code fails to combine on line q2, since calling length() on an int is notallowed, and option C is correct. The rest of the lines compile without issue. Note that callingparallel() on an already parallel stream is allowed, and it may in fact return the same object.
What statements about the following code snippet are true? (Choose all that apply.)
Object o1 = new Object(); Object o2 = new Object(); var service = Executors.newFixedThreadPool(2);
var f1 = service.submit(() -> { synchronized (o1) { synchronized (o2) { System.out.print("Tortoise"); } } });
var f2 = service.submit(() -> { synchronized (o2) { synchronized (o1) { System.out.print("Hare"); } } });
f1. get(); f2. get();
A. The code will always output Tortoise followed by Hare.
B. The code will always output Hare followed by Tortoise.
C. If the code does output anything, the order cannot be determined.
D. The code does not compile.
E. The code compiles but may produce a deadlock at runtime.
F. The code compiles but may produce a livelock at runtime.
G. It compiles but throws an exception at runtime.
C, E.
The code compiles without issue, so option D is incorrect. Since both tasks are submitted to thesame thread executor pool, the order cannot be determined, so options A and B are incorrect, and
option C is correct. The key here is that the order in which the resources o1 and o2 are synchronizedcould result in a deadlock. For example, if the first thread gets a lock on o1 and the second thread getsa lock on o2 before either thread can get their second lock, then the code will hang at runtime, makingoption E correct. The code cannot produce a livelock, since both threads are waiting, so option F isincorrect. Finally, if a deadlock does occur, an exception will not be thrown, so option G is incorrect.