Create worker threads using Runnable and Callable, manage the thread life cycle, including automations provided by different Executor services and concurrent API.
Develop thread-safe code, using different locking mechanisms and concurrent API.
Process Java collections concurrently including the use of parallel streams.
1. The correct answer is C.
Explanation:
Thread thread = new Thread(); thread.start(task);
start
method does not take a Runnable
argument. The correct way to associate the Runnable
task with the thread is to pass it to the Thread
constructor.Thread thread = new Thread(task).run();
run()
directly on the thread does not start a new thread. It simply executes the run
method in the current thread.Thread thread = new Thread(task); thread.start();
Thread
object with the Runnable
task and then starts the thread by calling start()
.Thread thread = new Thread(); task.run();
Runnable
task with it. Additionally, calling task.run()
directly does not start a new thread; it runs the task in the current thread.Thread thread = Thread.start(task);
Thread.start
is not a valid static method. The start
method is an instance method that must be called on a Thread
object.2. The correct answer is B.
Explanation:
synchronized (this) { counter++; }
this
cannot be used in a static context. In the main
method, this
is not available. For a static field like counter
, you need to synchronize on a static object or class.synchronized (Main.class) { counter++; }
Main.class
ensures that only one thread can enter the synchronized block at a time for all instances of Main
, which is appropriate for protecting static fields like counter
.synchronized (task) { counter++; }
task
is a Runnable
object, and synchronizing on it does not effectively control access to the shared static field counter
.synchronized (counter) { counter++; }
counter
is a primitive type (int
), and you cannot synchronize on a primitive type. Synchronization requires an object.synchronized (System.out) { counter++; }
System.out
is not related to controlling access to counter
. It would also interfere with other potential uses of System.out
.3. The correct answers are B and C.
Explanation:
AtomicInteger
is part of the java.util.concurrent.atomic
package, but it does not provide atomic operations for increment and decrement.
AtomicInteger
provides atomic operations for increment and decrement, such as incrementAndGet()
and decrementAndGet()
.AtomicReference
can only be used with reference types, not primitive types.
AtomicReference
is designed to work with reference types and cannot be used with primitive types directly.AtomicLong
supports atomic operations on long
values, including getAndIncrement()
and compareAndSet()
methods.
AtomicLong
provides atomic operations on long
values, including getAndIncrement()
and compareAndSet()
methods.AtomicBoolean
can be used to perform atomic arithmetic operations on boolean
values.
AtomicBoolean
is used for atomic updates to boolean
values, but it does not support atomic arithmetic operations.4. The correct answer is A.
Explanation:
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
finally
block, which is the proper use of the Lock
interface.lock.lock();
count++;
lock.unlock();
lock.lock()
and lock.unlock()
, the lock will not be released, potentially causing a deadlock.try {
lock.lock(() -> {
count++;
});
} finally {
lock.unlock();
}
lock.lock()
call.synchronized(lock) {
count++;
}
synchronized
block is used with the lock
object itself, which is not the correct usage of the Lock
interface and does not provide the intended functionality.5. The correct answer is D.
Explanation:
executor.shutdownNow();
executor.awaitTermination(1, TimeUnit.MINUTES);
shutdownNow()
is called first, which attempts to stop all active tasks immediately. The subsequent awaitTermination()
call is unnecessary and will not work as intended because shutdownNow()
already attempts to terminate the tasks.executor.awaitTermination(1, TimeUnit.MINUTES);
executor.shutdown();
awaitTermination()
is called before shutdown()
. The awaitTermination()
method should only be called after initiating an orderly shutdown with shutdown()
.executor.shutdown();
executor.shutdownNow();
shutdownNow()
immediately after shutdown()
will cancel all running tasks, making the shutdown()
call redundant.executor.shutdown();
try {
if (!executor.awaitTermination(1, TimeUnit.MINUTES)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
InterruptedException
.6. The correct answer is D.
Explanation:
Future<Integer> future = executor.submit(task);
executor.shutdown();
Integer result = future.get();
System.out.println(result);
executor.shutdown()
is called before getting the result. While this sequence may work in some cases, it’s better practice to shut down the executor after ensuring all tasks have completed and results have been processed.Future<Integer> future = executor.submit(task);
Integer result = future.get();
executor.shutdownNow();
System.out.println(result);
executor.shutdownNow()
immediately attempts to stop all active tasks, which is unnecessary and potentially harmful right after retrieving the result.Future<Integer> future = executor.submit(task);
System.out.println(future.get(1, TimeUnit.SECONDS));
executor.shutdown();
Future.get(long timeout, TimeUnit unit)
without handling possible exceptions (TimeoutException
, InterruptedException
, ExecutionException
) is not a good practice, and it doesn’t ensure the executor is properly shut down if an exception occurs.Future<Integer> future = executor.submit(task);
try {
Integer result = future.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
Callable
task, retrieves the result using Future.get()
, handles any InterruptedException
or ExecutionException
that might be thrown, and ensures the executor is shut down properly in the finally
block.7. The correct answer is A.
Explanation:
ConcurrentHashMap
allows concurrent read and write operations, and retrieval operations do not block even when updates are being made.
ConcurrentHashMap
is designed to handle concurrent access, allowing multiple threads to read and write simultaneously without blocking read operations during updates.CopyOnWriteArrayList
is optimized for scenarios with a high number of write operations compared to read operations.
CopyOnWriteArrayList
is optimized for scenarios where read operations are far more frequent than write operations because it creates a new copy of the array on each write, which can be costly if writes are frequent.ConcurrentSkipListSet
does not kept elements sorted.
ConcurrentSkipListSet
keep elements according to their natural ordering, or by a Comparator
provided at set creation time.BlockingQueue
implementations like LinkedBlockingQueue
allow elements to be added and removed concurrently without any internal locking mechanisms.
BlockingQueue
implementations like LinkedBlockingQueue
do use internal locking mechanisms to handle concurrent access safely.8. The correct answer is B.
Explanation:
9. The correct answer is B.
Explanation:
int sum = numbers.parallelStream().reduce(1, Integer::sum);
System.out.println(sum);
1
as the identity value. The identity value for sum should be 0
, as it is the neutral element for addition. Starting the reduction with 1
will result in an incorrect sum that is incremented by 1
.int sum = numbers.parallelStream().reduce(0, Integer::sum).collect();
System.out.println(sum);
parallelStream()
to create a parallel stream and the reduce
method with the identity value 0
and the method reference Integer::sum
to sum the elements.int sum = numbers.stream().reduce(0, Integer::sum);
System.out.println(sum);
stream()
) instead of a parallel stream. While it correctly sums the elements, it does not demonstrate the use of a parallel stream as specified in the question.int sum = numbers.parallelStream().collect(reduce(0, Integer::sum));
System.out.println(sum);
collect()
method in combination with reduce()
, which is not the correct syntax. The collect()
method is used for mutable reduction and is typically used with collectors, not with the reduce()
operation directly.Do you like what you read? Would you consider?
Do you have a problem or something to say?