Lesson 27 • Advanced
Concurrency Utilities: Locks, Semaphores & Latches
Coordinate threads safely with ReentrantLock, Semaphore, and CountDownLatch.
Before You Start
You must understand Multithreading (Lesson 26) — thread creation, synchronization, and race conditions. This lesson builds on those concepts with more powerful coordination tools from java.util.concurrent.
What You'll Learn
- ✅ ReentrantLock vs synchronized
- ✅ ReadWriteLock for read-heavy workloads
- ✅ Semaphore for resource limiting
- ✅ CountDownLatch for waiting on tasks
- ✅ CyclicBarrier for phased computation
1️⃣ Beyond synchronized: Fine-Grained Concurrency
💡 Analogy: Building Security
synchronized is like a single master key — it locks the entire building. ReentrantLock is like a keycard system — you can try the door with a timeout, and the same person can re-enter. ReadWriteLock is like a museum — many visitors (readers) simultaneously, but closes for one renovator (writer). Semaphore is like a parking lot — only N cars allowed. CountDownLatch is like a relay race gate — all runners wait until count reaches zero.
Try It: ReentrantLock & ReadWriteLock
// ReentrantLock & ReadWriteLock
console.log("=== Locks ===\n");
// 1. ReentrantLock simulation
console.log("1. REENTRANTLOCK:");
class ReentrantLock {
constructor() { this.locked = false; this.owner = null; this.count = 0; }
lock(thread) {
if (this.owner === thread) { this.count++; console.log(" [" + thread + "] Re-entered (count: " + this.count + ")"); return true; }
if (!this.locked) { this.locked = true; this.owner = thread; this.count = 1; console.log(" [" + threa
...2️⃣ Semaphore & Resource Limiting
A Semaphore controls access to a limited resource by maintaining a set of permits. acquire() takes a permit (blocks if none available), release() returns one. Common use cases: connection pools, rate limiters, and bounded resource access.
Try It: Semaphore & CountDownLatch
// Semaphore & CountDownLatch
console.log("=== Semaphore & Latch ===\n");
// 1. Semaphore — connection pool
console.log("1. SEMAPHORE (Connection Pool, 3 permits):");
class Semaphore {
constructor(permits) { this.permits = permits; this.max = permits; }
acquire(thread) {
if (this.permits > 0) { this.permits--; console.log(" [" + thread + "] Acquired ✅ (available: " + this.permits + "/" + this.max + ")"); return true; }
console.log(" [" + thread + "] Waiting ⏳ (pool ful
...Try It: Rate Limiter with Semaphore
// Real-World: Rate Limiter
console.log("=== Rate Limiter with Semaphore ===\n");
class RateLimiter {
constructor(maxRequestsPerSecond) {
this.permits = maxRequestsPerSecond;
this.max = maxRequestsPerSecond;
this.log = [];
}
tryAcquire(requestId) {
if (this.permits > 0) {
this.permits--;
this.log.push({ id: requestId, status: "allowed" });
return true;
}
this.log.push({ id: requestId, status: "reject
...Common Mistakes
unlock() in a finally block.Pro Tips
💡 tryLock() with timeout prevents deadlocks: if (lock.tryLock(5, TimeUnit.SECONDS)).
💡 StampedLock (Java 8+) is faster than ReadWriteLock — supports optimistic reads without acquiring a lock.
💡 Semaphore as rate limiter: Create with N permits and use tryAcquire() to limit API calls.
📋 Quick Reference
| Utility | API | Use Case |
|---|---|---|
| ReentrantLock | lock() / unlock() | Flexible mutual exclusion |
| Semaphore | acquire() / release() | Limit concurrent access |
| CountDownLatch | countDown() / await() | Wait for N completions |
| CyclicBarrier | await() | Sync threads at checkpoint |
| ReadWriteLock | readLock() / writeLock() | Read-heavy concurrency |
🎉 Lesson Complete!
You can now coordinate threads with locks, semaphores, and barriers!
Next: CompletableFuture & async programming.
Sign up for free to track which lessons you've completed and get learning reminders.