DEV Community

Rohit Kori
Rohit Kori

Posted on

Concurrency Q&A

Java Concurrency — Interview Ready Q&A

2. Can we make an Array volatile in Java?

Yes, but volatile applies to the array reference, not the individual elements.

private volatile int[] arr;
Enter fullscreen mode Exit fullscreen mode

If one thread replaces the array:

arr = new int[]{1, 2, 3};
Enter fullscreen mode Exit fullscreen mode

other threads will see the updated array reference.

But:

arr[0] = 10;
Enter fullscreen mode Exit fullscreen mode

is not made thread-safe by volatile.

Interview line: "volatile guarantees visibility of the array reference, not atomicity or visibility guarantees for individual array elements."


3. Can you write code for Double-Checked Locking Singleton?

public class Singleton {

    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {

        if (instance == null) {

            synchronized (Singleton.class) {

                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Why volatile?

Object creation involves multiple steps, and without volatile, instruction reordering could allow another thread to observe a partially constructed object.

Interview line: "We use double checking to avoid synchronization overhead after initialization, and volatile guarantees safe publication."


4. What happens if the object you're synchronizing on is null?

Object lock = null;

synchronized (lock) {
    // code
}
Enter fullscreen mode Exit fullscreen mode

It throws:

NullPointerException
Enter fullscreen mode Exit fullscreen mode

You cannot synchronize on null because null has no object monitor.

Interview line: "The JVM requires a valid object monitor, and null doesn't have one."


5. Why doesn't ConcurrentHashMap Iterator throw ConcurrentModificationException?

ConcurrentHashMap iterators are designed for concurrent modifications.

They are weakly consistent:

  • Don't throw ConcurrentModificationException
  • Can reflect some modifications made during iteration
  • Don't represent a fixed snapshot
  • Continue safely while other threads modify the map

Example:

ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();

for (Integer key : map.keySet()) {
    map.put(10, "value");
}
Enter fullscreen mode Exit fullscreen mode

This doesn't fail with ConcurrentModificationException.

Interview line: "ConcurrentHashMap provides weakly consistent iterators that can safely operate while the map is being modified concurrently."


6. What is Busy Spin Waiting? What is the benefit?

Busy spinning means repeatedly checking a condition instead of putting the thread to sleep.

while (!condition) {
    // keep checking
}
Enter fullscreen mode Exit fullscreen mode

The thread continuously consumes CPU.

Benefit

Very low latency because there is no thread parking, wake-up, or context-switch overhead.

Useful when:

  • Expected wait time is extremely short
  • Low latency is more important than CPU efficiency

Interview line: "Busy spinning trades CPU consumption for very low latency."


7. If start() calls run(), why not call run() directly?

Actually, start() does not simply call run() on the current thread.

thread.start();
Enter fullscreen mode Exit fullscreen mode

starts a new thread, and that thread executes run().

Whereas:

thread.run();
Enter fullscreen mode Exit fullscreen mode

is just a normal method call and executes on the current thread.

Example

Thread t = new Thread(() -> {
    System.out.println(Thread.currentThread().getName());
});

t.start(); // New thread

t.run();   // Current thread
Enter fullscreen mode Exit fullscreen mode

Interview line: "start() creates a new execution thread; directly calling run() does not."


8. Why should we call wait() and notify() in a loop and not an if block?

Correct:

synchronized (lock) {

    while (!condition) {
        lock.wait();
    }

    // proceed
}
Enter fullscreen mode Exit fullscreen mode

Not:

synchronized (lock) {

    if (!condition) {
        lock.wait();
    }

    // proceed
}
Enter fullscreen mode Exit fullscreen mode

Why?

Because waking up does not guarantee that the condition is still true.

There can also be spurious wakeups.

After waking up, the thread should always re-check the condition.

Interview line: "We use while because waking up doesn't guarantee that the condition is true; the thread must re-check it."


Few More Questions

9. If asked to synchronize between 2 threads or 100 threads, which is harder and why?

100 threads is harder to reason about.

With 2 threads, there are relatively few possible execution orders.

With 100 threads, you have:

  • Many possible interleavings
  • More contention
  • Race conditions
  • Deadlocks
  • Starvation
  • Visibility problems
  • More difficult debugging and testing

The synchronization primitive itself may be the same, but reasoning about correctness becomes much harder.

Interview line: "The difficulty comes from the huge number of possible thread interleavings and interactions."


10. Tell me three problems you usually face in a concurrent environment.

1. Race Condition

Multiple threads access shared state concurrently and produce an incorrect result.

Example:

count++;
Enter fullscreen mode Exit fullscreen mode

is not atomic.

2. Deadlock

Two or more threads wait indefinitely for locks held by each other.

Thread A → Lock 1 → waits for Lock 2
Thread B → Lock 2 → waits for Lock 1
Enter fullscreen mode Exit fullscreen mode

3. Visibility Problem

One thread updates a variable, but another thread doesn't immediately see the updated value.

Can be addressed using mechanisms such as:

  • volatile
  • synchronized
  • Lock
  • Atomic classes

Interview line: "The three common problems are race conditions, deadlocks, and visibility issues."


11. What happens if you add a task to a Fixed Thread Pool and the worker queue is full?

There is an important distinction.

Executors.newFixedThreadPool()

ExecutorService executor =
        Executors.newFixedThreadPool(10);
Enter fullscreen mode Exit fullscreen mode

The default implementation uses an unbounded LinkedBlockingQueue.

Therefore, the queue normally doesn't become full.

Bounded ThreadPoolExecutor

For example:

ExecutorService executor =
    new ThreadPoolExecutor(
        10,
        10,
        0,
        TimeUnit.MILLISECONDS,
        new ArrayBlockingQueue<>(100)
    );
Enter fullscreen mode Exit fullscreen mode

If:

  • All 10 workers are busy, and
  • The queue contains 100 tasks

then the next task is rejected.

The behavior depends on the configured RejectedExecutionHandler.

For example:

new ThreadPoolExecutor.AbortPolicy()
Enter fullscreen mode Exit fullscreen mode

throws:

RejectedExecutionException
Enter fullscreen mode Exit fullscreen mode

Other policies include:

  • AbortPolicy
  • CallerRunsPolicy
  • DiscardPolicy
  • DiscardOldestPolicy

Interview line: "If all workers are busy and a bounded queue is full, the task is handled by the configured RejectedExecutionHandler."


12. What happens if an exception is thrown inside a Thread?

For a raw Thread:

Thread t = new Thread(() -> {
    throw new RuntimeException("Something went wrong");
});
Enter fullscreen mode Exit fullscreen mode

If the exception is uncaught:

  1. The thread terminates.
  2. The exception goes to the thread's UncaughtExceptionHandler.
  3. If no handler is configured, the default handler generally prints the stack trace.

Example:

t.setUncaughtExceptionHandler((thread, exception) -> {
    System.out.println(
        "Exception: " + exception.getMessage()
    );
});
Enter fullscreen mode Exit fullscreen mode

Important: ExecutorService

If you use:

Future<?> future = executor.submit(task);
Enter fullscreen mode Exit fullscreen mode

the exception is captured by the Future.

It becomes visible when:

future.get();
Enter fullscreen mode Exit fullscreen mode

is called.

You'll get:

ExecutionException
Enter fullscreen mode Exit fullscreen mode

Interview line: "For a raw Thread, an uncaught exception terminates that thread and is handled by the UncaughtExceptionHandler. With ExecutorService, submitted-task exceptions can be captured in the Future."


🔥 30-Second Revision

Question Key Point
volatile Array Reference is volatile; elements aren't
Double-Checked Locking volatile + double check + synchronized
Synchronize on null NullPointerException
ConcurrentHashMap Iterator Weakly consistent; no CME
Busy Spin CPU usage for lower latency
start() vs run() start() → new thread; run() → current thread
wait() in loop Re-check condition + handle spurious wakeups
2 vs 100 threads More interleavings and contention
Concurrency problems Race condition, deadlock, visibility
Full thread-pool queue Rejection handler
Thread exception Thread terminates; uncaught handler handles it

Top comments (0)