1. The Hook (The "Byte-Sized" Intro)
In a Nutshell: ReentrantLock offers more flexibility than synchronized: tryLock() (non-blocking), timed locks, interruptible locks, fairness policies. ReadWriteLock allows multiple readers OR single writer (better for read-heavy scenarios). Condition variables replace wait/notify. Atomic variables (AtomicInteger, AtomicReference) provide lock-free thread-safety via CAS (Compare-And-Swap) operations—faster than synchronized for simple operations!
Think of hotel rooms. synchronized = one master key. ReentrantLock = smart lock with PIN codes, timeouts, guest list. ReadWriteLock = library (many readers, one writer). Atomic = self-service kiosk (no queue needed)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- synchronized: Automatic door lock (simple, inflexible)
- ReentrantLock: Smart lock (timeout, try without blocking)
- Atomic: Vending machine (no lock needed, CAS = coin slot accepts only if price matches)
3. Technical Mastery (The "Deep Dive")
ReentrantLock vs synchronized
// synchronized (implicit lock)
public synchronized void method() {
// Locked automatically
}
// ReentrantLock (explicit lock)
private final Lock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// Critical section
} finally {
lock.unlock(); // MUST unlock in finally!
}
}Atomic Variables
// ❌ synchronized (heavyweight)
private int count = 0;
public synchronized void increment() {
count++;
}
// ✅ Atomic (lock-free)
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // CAS operation
}4. Interactive & Applied Code
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
public class AdvancedSynchronizationDemo {
public static void main(String[] args) {
demonstrateReentrantLock();
demonstrateReadWriteLock();
demonstrateAtomicVariables();
}
static void demonstrateReentrantLock() {
System.out.println("=== REENTRANT LOCK ===");
ReentrantLock lock = new ReentrantLock();
// Try-lock with timeout
try {
if (lock.tryLock(100, java.util.concurrent.TimeUnit.MILLISECONDS)) {
try {
System.out.println("Lock acquired");
// Critical section
} finally {
lock.unlock();
}
} else {
System.out.println("Could not acquire lock");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// Fair lock (FIFO order)
ReentrantLock fairLock = new ReentrantLock(true); // fair=true
}
static void demonstrateReadWriteLock() {
System.out.println("\n=== READ-WRITE LOCK ===");
ReadWriteLock rwLock = new ReentrantReadWriteLock();
// Multiple readers can hold lock simultaneously
Runnable reader = () -> {
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " reading");
Thread.sleep(1000);
} catch (Exception e) {
} finally {
rwLock.readLock().unlock();
}
};
// Only one writer at a time
Runnable writer = () -> {
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " writing");
Thread.sleep(1000);
} catch (Exception e) {
} finally {
rwLock.writeLock().unlock();
}
};
new Thread(reader, "Reader-1").start();
new Thread(reader, "Reader-2").start();
new Thread(writer, "Writer").start();
}
static void demonstrateAtomicVariables() {
System.out.println("\n=== ATOMIC VARIABLES ===");
AtomicInteger counter = new AtomicInteger(0);
// incrementAndGet() - atomic operation
System.out.println("Increment: " + counter.incrementAndGet()); // 1
// compareAndSet() - CAS operation
boolean updated = counter.compareAndSet(1, 10);
System.out.println("CAS updated: " + updated); // true
System.out.println("Value: " + counter.get()); // 10
// AtomicReference for objects
AtomicReference<String> ref = new AtomicReference<>("Initial");
ref.compareAndSet("Initial", "Updated");
System.out.println("Reference: " + ref.get());
}
}
// Condition example
class BoundedBuffer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items = new Object[10];
private int count, putIndex, takeIndex;
public void put(Object item) throws InterruptedException {
lock.lock();
try {
while (count == items.length) {
notFull.await(); // Better than wait()
}
items[putIndex] = item;
putIndex = (putIndex + 1) % items.length;
count++;
notEmpty.signal(); // Better than notify()
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
Object item = items[takeIndex];
takeIndex = (takeIndex + 1) % items.length;
count--;
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}5. The Comparison & Decision Layer
| Feature | synchronized | ReentrantLock | Atomic |
|---|---|---|---|
| Lock acquisition | Automatic | Manual (lock/unlock) | Lock-free |
| Try-lock | ❌ No | ✅ Yes | N/A |
| Timeout | ❌ No | ✅ Yes | N/A |
| Fairness | ❌ No | ✅ Optional | N/A |
| Performance | Medium | Medium | Fast |
| Use case | Simple locking | Advanced needs | Simple counters |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "What is CAS (Compare-And-Swap) and why is it lock-free?" Answer: CAS is hardware-level atomic instruction: "Only update if current value equals expected."
// CAS pseudocode
boolean compareAndSet(int expect, int update) {
// ATOMIC at hardware level (single CPU instruction)
if (currentValue == expect) {
currentValue = update;
return true;
}
return false;
}
// Lock-free increment
public void increment() {
int current;
do {
current = count.get();
} while (!count.compareAndSet(current, current + 1));
// Retries if another thread changed value
}Lock-free = no thread ever blocks waiting for lock → better scalability!
Pro-Tip: Always unlock in finally block:
// ❌ DANGEROUS: Exception before unlock = lock never released!
lock.lock();
riskyOperation(); // Might throw exception
lock.unlock(); // Never reached!
// ✅ SAFE: unlock guaranteed
lock.lock();
try {
riskyOperation();
} finally {
lock.unlock(); // Always releases
}