OCP Java Concurrency Questions and Answers 1 — Questions and Answers
Question 1: An `ExecutorService` is actively running tasks. A separate thread calls the `shutdown()` method on the service. Immediately after, another attempt is made to submit a new task using the `submit()` method. What will be the result of this second submission attempt?
- The submission will be rejected, and a `RejectedExecutionException` will be thrown. (Correct answer)
- The task will be accepted and placed in the queue to be executed after the currently running tasks complete.
- The `submit()` call will block indefinitely until the `ExecutorService` has fully terminated.
- The new task will be executed immediately by the calling thread.
Correct answer: The submission will be rejected, and a `RejectedExecutionException` will be thrown.
The `ExecutorService.shutdown()` method initiates an orderly shutdown where previously submitted tasks are executed, but no new tasks will be accepted. Any attempt to submit a new task to the service after `shutdown()` has been called will result in a `RejectedExecutionException`.
Question 2: Which of the following statements most accurately describes a primary difference between `java.util.concurrent.CyclicBarrier` and `java.util.concurrent.CountDownLatch`?
- `CountDownLatch` is designed for a single use, whereas `CyclicBarrier` can be reset and reused after all waiting threads are released. (Correct answer)
- `CyclicBarrier` is limited to coordinating exactly two threads, while `CountDownLatch` can coordinate any number of threads.
- Threads wait on a `CyclicBarrier` by calling `countDown()`, and on a `CountDownLatch` by calling `await()`.
- Only `CountDownLatch` allows a timeout to be specified when waiting.
Correct answer: `CountDownLatch` is designed for a single use, whereas `CyclicBarrier` can be reset and reused after all waiting threads are released.
The most significant difference is that a `CyclicBarrier` is reusable. After the barrier is tripped (all parties have arrived), it can be reset to its initial state, either automatically or by calling `reset()`, for another round of coordination. A `CountDownLatch` is a one-time-use synchronizer; once its count reaches zero, it cannot be reset.
Question 3: A developer is creating a high-performance, thread-safe counter for a heavily used application. Which approach is generally preferred over using a `synchronized` method for a simple atomic increment operation?
- Using the `incrementAndGet()` method of `java.util.concurrent.atomic.AtomicInteger`. (Correct answer)
- Declaring the primitive integer counter as `volatile`.
- Using a `ReentrantLock` to protect the increment operation.
- Wrapping the integer in a `Collections.synchronizedMap`.
Correct answer: Using the `incrementAndGet()` method of `java.util.concurrent.atomic.AtomicInteger`.
`AtomicInteger` is specifically designed for this purpose. It uses low-level, non-blocking hardware instructions like Compare-And-Swap (CAS) to perform atomic operations. Under low to moderate contention, this is typically more performant than using a `synchronized` block or a `ReentrantLock`, which involves thread blocking and context switching overhead. A `volatile` variable only guarantees visibility, not atomicity for a read-modify-write operation like incrementing.
Question 4: In a multi-threaded application, you need a `Map` that will be read from very frequently by many threads but written to only occasionally. High throughput for read operations is critical. Which of the following is the most suitable thread-safe `Map` implementation for this scenario?
- A `HashMap` wrapped with `Collections.synchronizedMap()`
- `java.util.Hashtable`
- `java.util.concurrent.ConcurrentHashMap` (Correct answer)
- A `TreeMap` protected by a `ReadWriteLock`
Correct answer: `java.util.concurrent.ConcurrentHashMap`
`ConcurrentHashMap` is the ideal choice. It is designed for high concurrency and scalability. It achieves this by using a fine-grained locking mechanism (e.g., locking only specific 'buckets' or segments of the map) which allows multiple read operations to occur concurrently without any blocking. In contrast, `Collections.synchronizedMap()` and the legacy `Hashtable` use a single lock for the entire map, meaning only one thread can access it at a time for any operation, creating a bottleneck in read-heavy scenarios.
Question 5: What are the key differences in the method signatures of `java.lang.Runnable` and `java.util.concurrent.Callable`?
- The `run()` method of `Runnable` cannot return a value, while the `call()` method of `Callable` can. (Correct answer)
- The `call()` method of `Callable` cannot throw checked exceptions, while the `run()` method of `Runnable` can.
- `Runnable`'s `run()` method accepts an argument, while `Callable`'s `call()` method does not.
- `Runnable` has a `start()` method, whereas `Callable` has a `call()` method.
Correct answer: The `run()` method of `Runnable` cannot return a value, while the `call()` method of `Callable` can.
The `run()` method in the `Runnable` interface has a `void` return type. In contrast, the `call()` method in the `Callable<V>` interface returns a generic type `V`, allowing the asynchronous task to produce a result. Additionally, the `call()` method is declared to throw `Exception`, permitting checked exceptions to be propagated, which is not possible with `run()`.
Question 6: A shared instance of the following class is accessed by multiple threads. Which concurrency issue is most likely to occur? ```java public class TextBuilder { private StringBuilder content = new StringBuilder(); public void appendText(String text) { this.content.append(text); } public String toString() { return content.toString(); } } ```
- A `NullPointerException` when calling `toString()`.
- A deadlock if two threads call `appendText()` simultaneously.
- A race condition leading to corrupted or lost data within the `StringBuilder`. (Correct answer)
- A `ConcurrentModificationException` during the `append()` operation.
Correct answer: A race condition leading to corrupted or lost data within the `StringBuilder`.
The `java.lang.StringBuilder` class is not thread-safe. When multiple threads call the `appendText` method concurrently on the same `TextBuilder` instance, they are all attempting to modify the internal state of the single `StringBuilder` object. This creates a race condition, which can lead to unpredictable results, such as lost updates, garbled text, or an `ArrayIndexOutOfBoundsException` internally, because the operations on the internal character array are not atomic. The thread-safe alternative is `StringBuffer`.
An `ExecutorService` is actively running tasks.
A separate thread calls the `shutdown()` method on the service.
Immediately after, another attempt is made to submit a new task using the `submit()` method.
What will be the result of this second submission attempt?