Understanding Java's Synchronized Mechanism: From Bytecode to Monitor Objects

Java Object Structure

In the JVM, objects are organized into three memory regions:

  • Object Header

    • Mark Word: Stores object hash code, generation age, and lock status flags. This field is dynamically reused based on the object's state.
    • Klass Pointer: A reference to the class metadata, enabling the JVM to determine the object's class.
  • Instance Data

    • Contains the actual field values of the object, encluding inherited data from parent classes.
  • Padding

    • Ensures object alignment to 8-byte boundaries as required by the JVM. An empty object occupies 8 bytes due to this padding.

Synchronized Usage Scenarios

The synchronized keyword in Java can be applied in three primary ways:

  • Instance Methods: Acquires a lock on the current instance (this) java public class SynchronizedExample { public synchronized void method() { // Critical section } }

  • Static Methods: Acquires a lock on the class's Class object java public class SynchronizedExample { public static synchronized void staticMethod() { // Critical section } }

  • Code Blocks: Acquires a lock on a specified object java public class SynchronizedExample { public void method() { synchronized (new Object()) { // Critical section } } }

Memory Model Guarantees

Ordering

The JVM ensures that as-if-serial semantics are maintained. While optimizations may reorder instructions, single-thread execution results remain consistent, and data dependencies are preserved.

Visibility

The Java Memory Model (JMM) defines visibility rules for shared variables, ensuring changes are properly propagated between threads.

Atomicity

Synchronization guarantees atomicity by ensuring only one thread can execute a critical section at a time.

Key Synchronized Features

Reentrancy

Synchronized locks are reentrant, allowing a thread to acquire the same lock multiple times. A counter tracks lock acquisition depth, decrementing upon exit and releasing when it reaches zero.

Non-Interruptibility

Once a thread acquires a lock, other threads waiting for that lock cannot be interrupted until the lock is released.

Implementation Details

Bytecode Analysis

Examining the bytecode reveals how synchronization is implemented:

public class SynchronizedExample {
    public synchronized void method() {
        synchronized (new Object()) {
            // Critical section
        }
    }
}

Disassembling with javap -p -v -c shows:

  • For synchronized methods: The ACC_SYNCHRONIZED flag is set
  • For synchronized blocks: monitorenter and monitorexit instructions are used

Monitor Objects

The core of Java synchronization is the monitor mechanism, implemented in C++ as ObjectMonitor. Key fields include:

  • _count: Lock acquisition count
  • _recursions: Reentrancy counter
  • _owner: Current lock holder
  • _WaitSet: Threads waiting on the monitor
  • _EntryList: Threads blocked waiting for the monitor

Lock Evolution Process

Biased Locking (Java 6+ default)

  • First thread to access an object "biases" the lock
  • Thread ID is stored in the Mark Word
  • Subsequent accesses by the same thread require no synchronization
  • CAS operations are used for lock acquisition

Lightweight Locking

  • If no bias exists or bias is revoked, lightweight locking is attempted
  • A lock record is created in the thread's stack frame
  • CAS operations are used to update the object's Mark Word
  • On failure, lock state is upgraded

Spin Locking

  • If lightweight locking fails, the thread spins briefly (default 10 times)
  • Avoids context switching by keeping the thread active
  • Reduces overhead compared to full thread blockign

Heavyweight Locking

  • After spin threshold is exceeded, the lock becomes heavyweight
  • Threads are blocked and require OS intervention for wakeup
  • Involves kernel transitions, which are resource-intensive

Synchronized vs. Lock Interface

Key differences:

  • Nature: synchronized is a JVM keyword; Lock is a JDK interface
  • Lock Release: synchronized is automatic; Lock requires manual release
  • Interruptibility: synchronized is non-interruptible; Lock suppotrs interruption
  • Status Checking: Lock allows checking if a thread holds the lock
  • Fairness: synchronized is non-fair; ReentrantLock can be configured for fairness
  • Read Locks: Lock interfaces support read-write locks for improved concurrency

The choice between synchronized and Lock depends on specific use cases and requirements.

Tags: java Concurrency Synchronization multithreading JVM

Posted on Wed, 24 Jun 2026 18:30:14 +0000 by gmcalp