Skip to content
KAVRIQ
Interview contents

Java Concurrency – Interview & Real-World Scenarios

This final part in our Java Concurrency series focuses on interview-specific pitfalls and real-world concurrency scenarios. You’ll learn how to avoid traps like deadlocks, design producer–consumer pipelines, and compare concurrency models.


1. Common Interview Pitfalls

Singleton and Concurrency

A common interview question: “How do you make a Singleton thread-safe in Java?”

  • Incorrect (not thread-safe):
public class BadSingleton {
private static BadSingleton instance;
public static BadSingleton getInstance() {
if (instance == null) {
instance = new BadSingleton();
}
return instance;
}
}
  • Thread-safe (synchronized method):
public class SafeSingleton {
private static SafeSingleton instance;
public static synchronized SafeSingleton getInstance() {
if (instance == null) {
instance = new SafeSingleton();
}
return instance;
}
}
  • Better (double-checked locking with volatile):
public class DCLSingleton {
private static volatile DCLSingleton instance;
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}

Deadlock Avoidance

  • Scenario: Two threads hold locks in opposite order.
  • Fix: Always acquire locks in a consistent global order or use tryLock() with timeout.

Other Interview Traps

  • Misusing volatile to guarantee atomicity (it doesn’t).
  • Assuming HashMap is thread-safe (it’s not).
  • Forgetting to shut down an ExecutorService.

2. Designing a Producer–Consumer Pipeline

A classic concurrency problem, common in interviews and real systems.

Example with BlockingQueue

import java.util.concurrent.*;
class ProducerConsumer {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
Runnable producer = () -> {
try {
for (int i = 0; i < 10; i++) {
queue.put(i);
System.out.println("Produced " + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
Runnable consumer = () -> {
try {
while (true) {
int item = queue.take();
System.out.println("Consumed " + item);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
new Thread(producer).start();
new Thread(consumer).start();
}
}
  • Why BlockingQueue?
    • Handles synchronization internally.
    • Blocks producers if full, consumers if empty.
    • Avoids manual wait()/notify() pitfalls.

3. Comparing Concurrency Models

Threads (Low-Level Model)

  • Explicitly create and manage threads.
  • Full control but more error-prone.
  • Good for fine-grained concurrency, but harder to scale.

Async/Callback Model

  • Uses event loops and futures.
  • Example: CompletableFuture in Java.
CompletableFuture.supplyAsync(() -> "data")
.thenApply(String::toUpperCase)
.thenAccept(System.out::println);
  • Great for I/O-bound tasks.
  • Drawback: Callback hell if not structured well.

Actor Model

  • Each actor encapsulates state and communicates via messages.
  • Popularized by Akka (Java/Scala).
  • Scales horizontally across cores/nodes.
  • Great for highly concurrent, distributed systems.
  • Tradeoff: Learning curve, debugging message-passing.

4. Best Practices for Interviews & Real Systems

  • Always discuss tradeoffs (e.g., synchronized vs Lock).
  • Mention scalability and fairness in locks.
  • For pipelines, suggest backpressure handling (bounded queues).
  • Prefer immutable objects to reduce synchronization.
  • Highlight higher-level abstractions (ExecutorService, CompletableFuture, Streams).

5. Quick Interview Checklist

  • Explain thread safety in Singleton.
  • Avoid deadlocks → consistent lock ordering.
  • Know BlockingQueue producer–consumer.
  • Compare Thread vs Async vs Actor.
  • Show knowledge of CompletableFuture for async flows.

Conclusion

Concurrency is not just about writing threads — it’s about designing safe, scalable systems. For interviews, focus on correctness, clarity, and tradeoffs. In real-world systems, use higher-level abstractions and proven concurrency models to reduce complexity.