1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Synchronization prevents race conditions when multiple threads access shared mutable data. Use synchronized keyword on methods or blocks to create mutual exclusion (only one thread at a time). Each object has an intrinsic lock (monitor). synchronized method locks the object, synchronized(this) locks specific section, synchronized(Class.class) locks class-level.
- Critical: Synchronization adds performance overhead—use only when necessary!
Think of single bathroom shared by multiple people. Synchronization = locking the door. Only one person inside at a time (mutual exclusion). Others wait outside (blocked)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy: The Bank Account
Without synchronization = both ATMs withdraw simultaneously from $100 account → both see $100, both dispense $100 → account goes to -$100 (race condition)!
With synchronization = ATM locks account, withdraws, unlocks → safe!
3. Technical Mastery (The "Deep Dive")
Race Condition Example
java
class Counter {
private int count = 0;
// ❌ NOT THREAD-SAFE
public void increment() {
count++; // 3 operations: read, increment, write
}
// ✅ THREAD-SAFE
public synchronized void incrementSafe() {
count++;
}
}Synchronized Methods vs Blocks
java
// Synchronized METHOD (locks entire object)
public synchronized void method() {
// Entire method locked
}
// Synchronized BLOCK (locks specific section)
public void method() {
// Unsynchronized code
synchronized(this) {
// Only this section locked
}
// More unsynchronized code
}4. Interactive & Applied Code
java
class BankAccount {
private int balance = 1000;
// WITHOUT synchronization (race condition!)
public void withdrawUnsafe(int amount) {
if (balance >= amount) {
// Simulate delay (thread switch happens here!)
try { Thread.sleep(10); } catch (Exception e) {}
balance -= amount;
System.out.println("Withdrew " + amount + ", balance: " + balance);
}
}
// WITH synchronized method
public synchronized void withdraw(int amount) {
if (balance >= amount) {
try { Thread.sleep(10); } catch (Exception e) {}
balance -= amount;
System.out.println("Withdrew " + amount + ", balance: " + balance);
} else {
System.out.println("Insufficient funds!");
}
}
public synchronized int getBalance() {
return balance;
}
}
public class SynchronizationDemo {
public static void main(String[] args) throws Exception {
// Demonstrate race condition
System.out.println("=== WITHOUT SYNCHRONIZATION ===");
BankAccount account1 = new BankAccount();
Thread t1 = new Thread(() -> account1.withdrawUnsafe(600));
Thread t2 = new Thread(() -> account1.withdrawUnsafe(600));
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("Final balance: " + account1.getBalance()); // ❌ Negative!
// With synchronization
System.out.println("\n=== WITH SYNCHRONIZATION ===");
BankAccount account2 = new BankAccount();
Thread t3 = new Thread(() -> account2.withdraw(600));
Thread t4 = new Thread(() -> account2.withdraw(600));
t3.start(); t4.start();
t3.join(); t4.join();
System.out.println("Final balance: " + account2.getBalance()); // ✅ Safe!
// Synchronized block example
demonstrateSynchronizedBlock();
}
static void demonstrateSynchronizedBlock() {
Object lock = new Object();
Runnable task = () -> {
synchronized(lock) {
System.out.println(Thread.currentThread().getName() + " acquired lock");
try { Thread.sleep(1000); } catch (Exception e) {}
System.out.println(Thread.currentThread().getName() + " released lock");
}
};
new Thread(task, "Thread-1").start();
new Thread(task, "Thread-2").start();
}
}5. The Comparison & Decision Layer
| Approach | Lock Scope | Use When |
|---|---|---|
| synchronized method | Entire object | Simple, whole method needs protection |
| synchronized block | Specific section | Only part of method needs locking |
| static synchronized | Class-level lock | Protecting static fields |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "What's wrong with this code?"
java
public void method() {
synchronized(new Object()) { // ❌ NEW object each time!
count++;
}
}Answer: Creates a new lock object every time! No mutual exclusion—every thread gets its own lock. Should synchronize on the same object:
java
private final Object lock = new Object(); // ✅ Shared lock
public void method() {
synchronized(lock) {
count++;
}
}Pro-Tip: Minimize synchronized scope:
java
// ❌ BAD: Locks for too long
public synchronized void process() {
expensiveOperation(); // Doesn't need protection
count++; // Only this needs protection
anotherExpensiveOp(); // Doesn't need protection
}
// ✅ GOOD: Lock only critical section
public void process() {
expensiveOperation();
synchronized(this) {
count++; // Minimal lock time
}
anotherExpensiveOp();
}