Lesson Completion
Back to course

Synchronization: Preventing Race Conditions

Advanced
25 minutes4.7Java

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

ApproachLock ScopeUse When
synchronized methodEntire objectSimple, whole method needs protection
synchronized blockSpecific sectionOnly part of method needs locking
static synchronizedClass-level lockProtecting 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(); }

Topics Covered

Java FundamentalsConcurrency

Tags

#java#multithreading#threads#concurrency#parallelism

Last Updated

2025-02-01