Skip to content

Core Java Internals and Concurrency Interview Questions

  • HashMap internals
  • ConcurrentHashMap
  • Hash collisions
  • HashMap resizing
  • ArrayList vs LinkedList
  • HashSet internals
  • Comparable vs Comparator
  • Immutable classes
  • StringBuilder vs StringBuffer
  • volatile keyword
  • synchronized vs ReentrantLock
  • ThreadPoolExecutor
  • Deadlocks
  • Race Conditions
  • AtomicInteger
  • CompletableFuture
  • Parallel Stream vs Stream
  • ForkJoinPool
  • ThreadLocal
  • Heap
  • Stack
  • Metaspace
  • Garbage Collection
  • Memory Leaks
public int secondLargest(int[] arr){
int first = Integer.MIN_VALUE;
int second = Integer.MIN_VALUE;
for(int num : arr){
if(num > first){
second = first;
first = num;
} else if(num > second && num != first){
second = num;
}
}
return second;
}
Map<Character,Integer> map = new HashMap<>();
for(char c : str.toCharArray()){
map.put(c, map.getOrDefault(c,0)+1);
}
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
System.out.println("Task running");
});

Future alone has three problems: it’s blocking (get() waits), doesn’t support chaining further work, and doesn’t support composing multiple futures together. CompletableFuture solves all three:

CompletableFuture.supplyAsync(() -> getData())
.thenApply(data -> process(data))
.thenAccept(System.out::println);
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Running in a separate thread");
});
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> result = f1.thenCombine(f2, (a, b) -> a + b);
result.thenAccept(System.out::println); // 30
future.exceptionally(ex -> "Error: " + ex.getMessage());
CompletableFuture<String> f = new CompletableFuture<>();
f.complete("Manually completed");
System.out.println(f.get());

Uses ForkJoinPool.commonPool() by default for async stages, unless a specific Executor is passed to supplyAsync/runAsync. Common real-world uses: parallel REST calls combined with thenCombine, async database calls, and background processing — works well with Spring Boot’s @Async.

CountDownLatch latch = new CountDownLatch(3);
latch.await();
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
// Producer
queue.put(data);
// Consumer
queue.take();
Feature synchronized ReentrantLock
Fairness No Yes
Interruptible No Yes
tryLock No Yes
  • Senior interviews expect internals-level explanations of HashMap, ConcurrentHashMap, and collision handling, not just usage.
  • Know when ReentrantLock is worth the extra complexity over synchronized: fairness, interruptibility, and tryLock.
  • Be fluent with the concurrency utility toolbox: ExecutorService, CompletableFuture, CountDownLatch, and BlockingQueue.