Home/Learn/Java A–Z/synchronized Keyword

synchronized Keyword

Intermediate
Concurrency

synchronized provides mutual exclusion using intrinsic locks — preventing race conditions on shared mutable state.

Overview

synchronized is Java's built-in mechanism for mutual exclusion and memory visibility. Every object has an intrinsic lock (monitor). A synchronized method or block acquires this lock; other threads trying to acquire the same lock block until it is released. synchronized also establishes a happens-before relationship — changes made in a synchronized block are visible to threads that subsequently acquire the same lock.

Synchronized Methods and Blocks

synchronized on an instance method locks on this. On a static method, it locks on the Class object. A synchronized block lets you specify any object as the lock — often a private final lock object.

Using a private lock object is better than locking on this because external code cannot accidentally acquire the same lock.

SafeCounter.java
public class SafeCounter {
    private int count = 0;
    private final Object lock = new Object(); // dedicated lock

    // Synchronized method — locks on 'this'
    public synchronized void increment() {
        count++;
    }

    // Synchronized block — locks on dedicated lock object
    public void decrement() {
        synchronized (lock) {
            count--;
        }
    }

    // Static synchronized — locks on SafeCounter.class
    private static int instances = 0;
    public static synchronized void registerInstance() {
        instances++;
    }

    public synchronized int get() { return count; }
}

// Now race-condition-free
SafeCounter counter = new SafeCounter();
List<Thread> threads = IntStream.range(0, 1000)
    .mapToObj(i -> new Thread(counter::increment))
    .collect(Collectors.toList());
threads.forEach(Thread::start);
for (Thread t : threads) t.join();
System.out.println(counter.get()); // Always 1000

wait(), notify(), notifyAll()

wait() releases the lock and suspends the current thread until another thread calls notify() or notifyAll() on the same object. These methods must be called inside a synchronized block.

Always call wait() in a loop (not an if) to guard against spurious wakeups and to re-check the condition after being woken.

BoundedBuffer.java
public class BoundedBuffer<T> {
    private final Queue<T> queue = new LinkedList<>();
    private final int capacity;

    public BoundedBuffer(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T item) throws InterruptedException {
        while (queue.size() == capacity) { // loop — not if
            wait(); // release lock, suspend
        }
        queue.add(item);
        notifyAll(); // wake up waiting consumers
    }

    public synchronized T take() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }
        T item = queue.poll();
        notifyAll(); // wake up waiting producers
        return item;
    }
}

Deadlock and How to Avoid It

A deadlock occurs when two or more threads wait for each other's locks indefinitely. Classic scenario: Thread A holds lock1, waits for lock2. Thread B holds lock2, waits for lock1.

Prevention strategies: always acquire locks in the same global order; use tryLock() with timeout (ReentrantLock); minimise lock scope; prefer higher-level concurrency utilities.

Deadlock.java
// DEADLOCK — acquiring locks in different orders
Object lock1 = new Object();
Object lock2 = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lock1) {
        Thread.sleep(50);
        synchronized (lock2) { /* ... */ } // waits for lock2
    }
});

Thread t2 = new Thread(() -> {
    synchronized (lock2) {
        Thread.sleep(50);
        synchronized (lock1) { /* ... */ } // waits for lock1
    }
});
// t1 and t2 will deadlock

// FIX — always acquire locks in the same order
Thread t1Fixed = new Thread(() -> {
    synchronized (lock1) {          // lock1 first
        synchronized (lock2) { /* ... */ }
    }
});
Thread t2Fixed = new Thread(() -> {
    synchronized (lock1) {          // lock1 first (same order)
        synchronized (lock2) { /* ... */ }
    }
});

Interactive Visualization

NEWRUNNABLERUNNINGBLOCKEDWAITINGTERMINATED
synchronized(lock)— free
main
RUNNING
t1
NEW
t2
NEW
main thread creates Thread t1 and Thread t2. Both are in NEW state.
1 / 6

Key Points to Remember

  • synchronized on instance method locks on this; on static method locks on Class.
  • Use a private final lock object instead of this for better encapsulation.
  • synchronized provides both mutual exclusion and memory visibility (happens-before).
  • Always call wait() in a loop to guard against spurious wakeups.
  • Deadlock prevention: acquire locks in a consistent global order, or use tryLock().

Practice synchronized Keyword in the Playground

Run and modify code directly in your browser - no setup needed.

Interview Questions

Sign in to ask Aria
1

What is the difference between a synchronized method and a synchronized block?

EasyTCS
2

What is a deadlock and how do you prevent it?

MediumAmazon
3

Why should wait() always be called in a loop?

MediumGoogle
4

What is the difference between notify() and notifyAll()?

MediumOracle
5

What is an intrinsic lock (monitor) in Java?

MediumMicrosoft

Ask Aria about synchronized Keyword

Your personal AI tutor — ask anything about this concept