Definition
- Thread and Runnable – the basic units of concurrent execution, where a Thread runs a Runnable task.
- synchronized and volatile – built-in mechanisms for mutual exclusion and memory visibility.
- ExecutorService – manages a pool of threads, decoupling task submission from execution.
- CountDownLatch – a one-time gate where N events count down and waiters unblock at zero.
- CyclicBarrier – a reusable sync point where N threads all wait for each other before proceeding.
- Semaphore – limits concurrent access to a resource by issuing a fixed number of permits.
- CompletableFuture – builds non-blocking async pipelines with chaining, composition, and error handling.
Think of National Bank of India's branch operations. Thread is a teller. Runnable is a task form. ExecutorService is the teller counter manager who assigns forms to available tellers. synchronized is the vault door lock (one teller at a time). CountDownLatch is "wait until all 5 departments finish their EOD reports before generating the final summary." CyclicBarrier is "all 10 audit teams must reach the checkpoint before any can proceed to the next phase." Semaphore is the parking lot with 5 spots: the 6th car waits until one leaves.
Understanding the Concepts
What are the main concurrency tools in Java?
Java's concurrency toolkit has grown significantly since Java 1.0. The tools form layers, from low-level primitives to high-level abstractions.
Low-level primitives
ThreadandRunnablefor basic execution.synchronizedfor mutual exclusion.volatilefor memory visibility.wait(),notify(),notifyAll()for thread signaling.
Mid-level (java.util.concurrent)
ExecutorServicefor thread pool management.Futurefor async results.ReentrantLockfor flexible locking withtryLock()andlockInterruptibly().Conditionfor targeted thread signaling.
High-level synchronizers
CountDownLatchfor waiting on N events.CyclicBarrierfor N threads synchronizing at a checkpoint.Semaphorefor limiting concurrent access.Phaserfor flexible multi-phase synchronization.
Concurrent collections
ConcurrentHashMapfor thread-safe maps with lock striping.CopyOnWriteArrayListfor read-heavy lists.BlockingQueuefor producer-consumer patterns.
Atomics
AtomicInteger,AtomicLongfor lock-free counters.LongAdder,LongAccumulatorfor high-contention accumulators.
Modern Java
CompletableFuture(Java 8) for async pipelines.- Virtual threads (Java 21) for lightweight concurrency.
How does CountDownLatch work?
CountDownLatch(N) starts with count N. Any thread can call countDown() to decrement. Threads calling await() block until the count reaches zero. It is one-time: once triggered, it stays at zero.
package com.bank.service;
import java.util.concurrent.*;
public class LatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.submit(() -> { generateReport("Accounting"); latch.countDown(); });
pool.submit(() -> { generateReport("Loans"); latch.countDown(); });
pool.submit(() -> { generateReport("Compliance"); latch.countDown(); });
latch.await(); // blocks until count = 0
System.out.println("All reports ready. Generating EOD summary.");
pool.shutdown();
}
static void generateReport(String dept) {
System.out.println(dept + " report generated");
}
}
Accounting report generated
Loans report generated
Compliance report generated
All reports ready. Generating EOD summary.
How does CyclicBarrier work?
CyclicBarrier(N) blocks each of N threads until all N have called await(). When the Nth thread arrives, all are released simultaneously. It auto-resets for the next trip.
package com.bank.service;
import java.util.concurrent.*;
public class BarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("--- All 3 reached checkpoint ---");
});
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
final int id = i;
pool.submit(() -> {
try {
System.out.println("Auditor-" + id + " phase-1 done");
barrier.await(); // wait for others
System.out.println("Auditor-" + id + " phase-2 done");
barrier.await(); // reusable! wait again
System.out.println("Auditor-" + id + " complete");
} catch (Exception e) {
Thread.currentThread().interrupt();
}
});
}
pool.shutdown();
}
}
Auditor-0 phase-1 done
Auditor-1 phase-1 done
Auditor-2 phase-1 done
--- All 3 reached checkpoint ---
Auditor-0 phase-2 done
Auditor-1 phase-2 done
Auditor-2 phase-2 done
--- All 3 reached checkpoint ---
Auditor-0 complete
Auditor-1 complete
Auditor-2 complete
How does Semaphore work?
Semaphore limits concurrent access to a resource by issuing a fixed number of permits.
package com.bank.service;
import java.util.concurrent.*;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3); // max 3 concurrent
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
final int id = i;
pool.submit(() -> {
try {
semaphore.acquire();
System.out.println("Thread-" + id + " accessing database");
Thread.sleep(200); // simulate DB work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
}
});
}
pool.shutdown();
}
}
How is CountDownLatch different from CyclicBarrier?
| Feature | CountDownLatch | CyclicBarrier |
|---|---|---|
| Reusable | No (one-time) | Yes (auto-resets) |
| Who counts/waits | Different threads can count and wait | Same threads that wait are counted |
| Reset | Cannot reset | Automatic after each trip |
| Barrier action | None | Optional Runnable on trip |
| Use case | Wait for N events | N threads sync at a point |
| Broken state | N/A | BrokenBarrierException |
What is the concurrency toolkit summary?
| Tool | Purpose | Banking Example |
|---|---|---|
| Thread/Runnable | Basic execution | One teller per customer |
| ExecutorService | Pool management | 50-teller counter |
| synchronized | Mutual exclusion | Vault access |
| volatile | Visibility | Shutdown flag |
| ReentrantLock | Flexible locking | Account transfer lock |
| AtomicInteger | Lock-free counter | Transaction counter |
| ConcurrentHashMap | Thread-safe map | Session cache |
| BlockingQueue | Producer-consumer | Trade settlement queue |
| CountDownLatch | Wait for N events | EOD report aggregation |
| CyclicBarrier | Phase synchronization | Multi-phase audit |
| Semaphore | Limit concurrency | DB connection limit |
| CompletableFuture | Async pipelines | Loan approval workflow |
What are common interview follow-up questions?
- When would you use a Lock instead of synchronized? When you need tryLock(), lockInterruptibly(), or multiple conditions.
- What is the difference between fair and non-fair Semaphore? Fair respects FIFO order but has higher overhead.
- Can you use CountDownLatch.await() with a timeout? Yes,
await(timeout, unit)returns false on timeout.
Visual Overview
Java Concurrency Toolkit Layers ┌───────────────────────────────────────────────┐ │ Modern (Java 8+, 21+) │ │ CompletableFuture, Virtual Threads │ ├───────────────────────────────────────────────┤ │ High-Level Synchronizers │ │ CountDownLatch, CyclicBarrier, Semaphore │ ├───────────────────────────────────────────────┤ │ Concurrent Collections │ │ ConcurrentHashMap, BlockingQueue, COWAL │ ├───────────────────────────────────────────────┤ │ Mid-Level (java.util.concurrent) │ │ ExecutorService, Future, ReentrantLock │ ├───────────────────────────────────────────────┤ │ Low-Level Primitives │ │ Thread, Runnable, synchronized, volatile │ │ wait(), notify(), notifyAll() │ └───────────────────────────────────────────────┘
Key Takeaway
Java provides a layered concurrency toolkit. At the bottom: Thread, Runnable, synchronized, and volatile for basic concurrency. In the middle: ExecutorService for thread pool management, ReentrantLock for flexible locking, and Future for async results. At the top: CountDownLatch for waiting on N events, CyclicBarrier for synchronizing N threads at checkpoints, and Semaphore for limiting concurrent access.
CountDownLatch and CyclicBarrier are often confused. The latch is one-time: any threads count down, and different threads wait. Once the count reaches zero, it stays there. The barrier is reusable: the same threads that wait are the ones being counted, and it auto-resets after each trip.
Modern Java adds CompletableFuture for non-blocking async pipelines and virtual threads (Java 21) for lightweight, million-thread concurrency. Each tool solves a specific coordination problem. At National Bank of India, the EOD batch uses CountDownLatch(4) to wait for four department reports before archiving. The interest calculators use CyclicBarrier(10) to sync at checkpoints between phases. The database pool uses Semaphore(50) to limit concurrent connections.
Remember one sentence: Java concurrency is a layered toolkit where each tool solves a specific coordination problem.
---