The synchronized keyword in Java provides built-in thread synchronisation by ensuring that only one thread can execute a block of code or method at a time. When a thread attempts to enter a synchronized block, it must first acquire the monitor associated with the specified object.

Two Forms of Synchronisation

1. Synchronized Methods

public synchronized void method() {
    // Only one thread can execute this at a time
}
 
public static synchronized void staticMethod() {
    // Locks on the Class object
}

When a method is declared synchronized:

  • Instance methods lock on the instance (this)
  • Static methods lock on the Class object in the metaspace
  • The entire method body is the critical section

2. Synchronized Blocks

public void method() {
    synchronized(lockObject) {
        // Only one thread can execute this at a time
        // for this specific lockObject
    }
}

Synchronized blocks offer more flexibility:

  • You can specify which object to lock on
  • Allows finer-grained locking (shorter critical sections)
  • Multiple locks can be used within the same method

How Synchronisation Works

When a thread enters a synchronized block or method:

  1. Attempts to acquire the monitor of the specified object
  2. If unlocked or already owned by this thread and only this thread (reentrant):
    • Acquires the lock immediately
    • Mark Word transitions to Lightweight Lock state (00)
  3. If locked by another thread:
    • Thread blocks and waits
    • Mark Word inflates to Heavyweight Lock state (10)
    • Thread is added to the ObjectMonitor’s entry set

When the thread exits the synchronized block:

  • Releases the monitor
  • If there are waiting threads, only one is unparked to acquire the lock
  • If no contention, lock may deflate back to unlocked state

Reentrant Locking

Java’s synchronized is reentrant, meaning a thread that already owns a monitor can acquire it again:

public synchronized void outer() {
    inner(); // Same thread can enter
}
 
public synchronized void inner() {
    // This works! Thread already owns the monitor
}

Wait/Notify Mechanism

The synchronized keyword enables the wait/notify protocol for thread coordination:

synchronized(sharedObject) {
    while (!condition) {
        sharedObject.wait(); // Releases lock and waits
    }
    // Condition met, proceed
    sharedObject.notify(); // Wake one waiting thread
}

Key methods (Can be called while holding the monitor):

  • wait() - Releases the monitor and transitions into waiting state until notified
  • notify() - Wakes up a random thread waiting on this monitor
  • notifyAll() - Wakes up all threads waiting on this monitor

When wait() is called:

  • The lock must inflate to Heavyweight (ObjectMonitor needed)
  • Thread is moved from entry list to wait set
  • Monitor is released
  • Thread goes into waiting state and only wakes upon notify()
  • Upon notify(), thread moves back to entry list to reacquire lock