← Back to Home

synchronized Keyword

The synchronized keyword in Java is used to control concurrent access to shared resources. It ensures that only one thread at a time executes a critical section of code protected by the same lock, thereby preventing race conditions and ensuring data consistency. This is a very high-frequency interview topic in Java multithreading.

What Does synchronized Do?

  • Acquires a monitor lock before executing code
  • Allows mutual exclusion
  • Releases the lock automatically when execution exits
  • Ensures visibility of shared data (happens-before guarantee)

Monitor Lock (Intrinsic Lock)

  • Every Java object has a monitor (intrinsic) lock
  • synchronized uses this lock internally
  • A thread must own the lock to enter synchronized code

Ways to Use synchronized

1️⃣ Synchronized Instance Method

Locks the current object (this).

class Counter {
    int count = 0;

    synchronized void increment() {
        count++;
    }
}
          

✔ Only one thread per object can execute this method at a time

2️⃣ Synchronized Block (Recommended)

Locks a specific object and limits the critical section.

class Counter {
    int count = 0;

    void increment() {
        synchronized (this) {
            count++;
        }
    }
}
          

✔ Better performance

✔ Fine-grained locking

3️⃣ Synchronized Static Method

Locks the class-level lock.

class Counter {
    static int count = 0;

    static synchronized void increment() {
        count++;
    }
}
          

✔ Lock used: Counter.class

✔ Shared across all instances

Object Lock vs Class Lock (Interview Favorite)

Usage Lock Acquired
Instance synchronized method this
Synchronized block Specified object
Static synchronized method ClassName.class

How synchronized Works (Execution Flow)

  1. Thread requests lock
  2. If lock available → thread enters block/method
  3. Other threads are BLOCKED
  4. Thread exits synchronized code
  5. Lock is released automatically

Visibility Guarantee (Important)

  • Changes made inside synchronized block
  • Are visible to other threads after lock release

✔ Solves visibility issues

✔ Prevents stale reads

synchronized and Thread States

  • Waiting for lock → BLOCKED
  • Inside synchronized code → RUNNABLE

What synchronized Does NOT Do

  • Does NOT guarantee fairness
  • Does NOT prevent deadlocks
  • Does NOT improve performance
  • Does NOT replace good design

Common Mistakes

  • Synchronizing entire methods unnecessarily
  • Synchronizing on wrong object
  • Using this as lock in public APIs
  • Overusing synchronized causing performance issues

Best Practices (Production-Grade)

  • Prefer synchronized blocks over methods
  • Keep synchronized code minimal
  • Avoid synchronizing on mutable/public objects
  • Use private final lock objects if needed
private final Object lock = new Object();

synchronized (lock) {
    // critical section
}
          

synchronized vs volatile (Quick Contrast)

Aspect synchronized volatile
Mutual exclusion ✔ Yes ❌ No
Visibility ✔ Yes ✔ Yes
Atomicity ✔ Yes ❌ No
Performance Slower Faster

Advanced synchronized Scenarios

1. synchronized Method (Instance Lock)

class Counter {
    int count = 0;

    synchronized void increment() {
        count++;
    }
}
          

Explanation

  • Lock acquired on current object (this).
  • Only one thread per object can execute this method.

2. Non-synchronized Method Runs in Parallel

class Demo {
    synchronized void syncMethod() {
        System.out.println("Synchronized");
    }

    void normalMethod() {
        System.out.println("Not synchronized");
    }
}
          

Explanation

  • normalMethod() can run parallel with syncMethod().

3. synchronized Block (Preferred)

class Counter {
    int count = 0;

    void increment() {
        synchronized (this) {
            count++;
        }
    }
}
          

Why preferred

  • Locks only critical section.
  • Better performance than full method sync.

4. Synchronizing on a Custom Lock Object

class Counter {
    int count = 0;
    private final Object lock = new Object();

    void increment() {
        synchronized (lock) {
            count++;
        }
    }
}
          

Best practice

  • Avoid locking this.
  • Safer for large classes.

5. Two Threads, Same Object → Blocked

class Demo {
    synchronized void work() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        Demo d = new Demo();
        new Thread(d::work).start();
        new Thread(d::work).start();
    }
}
          

Explanation

  • Same object → same lock → one thread at a time.

6. Two Objects → No Blocking

class Demo {
    synchronized void work() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        new Thread(() -> new Demo().work()).start();
        new Thread(() -> new Demo().work()).start();
    }
}
          

Explanation

  • Different objects → different locks.

7. static synchronized Method (Class Lock)

class Printer {
    static synchronized void print() {
        System.out.println(Thread.currentThread().getName());
    }
}
          

Explanation

  • Lock acquired on Class object.
  • Blocks all threads across all instances.

8. Instance Lock vs Class Lock Together

class Test {
    synchronized void instanceMethod() {
        System.out.println("Instance lock");
    }

    static synchronized void staticMethod() {
        System.out.println("Class lock");
    }
}
          

Key Point

  • Instance lock ≠ Class lock.
  • Can run in parallel.

9. synchronized(this) vs synchronized(ClassName.class)

synchronized (this) {
    // object-level lock
}

synchronized (Test.class) {
    // class-level lock
}
          

10. synchronized with Runnable

class Task implements Runnable {
    int count = 0;

    public synchronized void run() {
        count++;
        System.out.println(count);
    }
}
          

Explanation

  • Synchronization applies on Runnable object.

11. synchronized and sleep() (Important Trap)

class Demo {
    synchronized void work() throws Exception {
        Thread.sleep(1000);
    }
}
          

Key Point

  • sleep() ❌ does NOT release lock.

12. synchronized and wait()

class Demo {
    synchronized void work() throws Exception {
        wait();
    }
}
          

Key Point

  • wait() ✅ releases lock.

13. Illegal Monitor State (Common Interview Trap)

class Demo {
    void work() throws Exception {
        // wait(); // ❌ IllegalMonitorStateException
    }
}
          

Rule

  • wait() / notify() must be inside synchronized.

14. notify() vs notifyAll()

class Demo {
    synchronized void resumeOne() {
        notify();
    }

    synchronized void resumeAll() {
        notifyAll();
    }
}
          

15. Over-Synchronization (Bad Practice)

class Demo {
    synchronized void hugeMethod() {
        // long running logic
    }
}
          

Problem

  • Reduces concurrency.
  • Use smaller synchronized blocks.

16. synchronized Is Reentrant

class Demo {
    synchronized void m1() {
        m2();
    }

    synchronized void m2() {
        System.out.println("Reentrant lock");
    }
}
          

Explanation

  • Same thread can re-enter lock.

17. synchronized with Multiple Methods

class Demo {
    synchronized void m1() {}
    synchronized void m2() {}
}
          

Explanation

  • Same object → same lock.
  • Only one method runs at a time.

18. ❌ Synchronizing on New Object (Useless)

synchronized (new Object()) {
    // No real synchronization
}
          

Why wrong

  • New object every time → no shared lock.

19. synchronized vs AtomicInteger

// synchronized
synchronized void increment() { count++; }

// atomic
AtomicInteger count = new AtomicInteger();
count.incrementAndGet();
          

Interview Note

  • Atomic → faster, lock-free.

20. Interview Summary – synchronized

synchronized

Key Points

  • Provides mutual exclusion.
  • Uses intrinsic locks.
  • Object lock vs Class lock.
  • Reentrant.
  • Can cause deadlock if misused.

Interview-Ready Answers

Short Answer

synchronized ensures that only one thread accesses a critical section at a time.

Detailed Answer

In Java, the synchronized keyword is used to acquire an intrinsic lock on an object or class before executing critical code. It prevents race conditions, ensures data consistency, and provides memory visibility guarantees by enforcing happens-before relationships.

Key Takeaway

synchronized protects shared data, not threads. Use it sparingly and precisely to achieve correctness without sacrificing performance.