Fundamentals 11 min read

How Java’s synchronized and CAS Translate into Bytecode: A Deep Dive

This article explores the JVM bytecode instructions behind Java’s synchronized keyword and CAS mechanism, detailing monitorenter/monitorexit, ACC_SYNCHRONIZED flags, static synchronization, and the underlying Unsafe.compareAndSetInt implementation with example code and analysis.

Cognitive Technology Team
Cognitive Technology Team
Cognitive Technology Team
How Java’s synchronized and CAS Translate into Bytecode: A Deep Dive

In Java, bytecode instructions are the foundation for JVM execution. This article explains how the synchronized keyword and CAS (Compare‑And‑Swap) mechanism are implemented at the bytecode level.

Bytecode Implementation of synchronized

The synchronized keyword relies on the JVM’s monitor lock and the object header. Depending on the usage, the JVM inserts two kinds of bytecode instructions.

1. Bytecode for synchronized blocks

When synchronized is applied to a code block, the JVM inserts monitorenter and monitorexit instructions to acquire and release the lock.

Example code:

public class SyncDemo {
    private final Object lock = new Object();

    public void syncBlock() {
        synchronized (lock) {
            System.out.println("Sync Block");
        }
    }
}

Decompiled with javap -c:

public void syncBlock();
   Code:
     0: aload_0
     1: getfield      #2 // Field lock:Ljava/lang/Object;
     4: dup
     5: astore_1
     6: monitorenter            // try to acquire lock
     7: getstatic     #3 // Field java/lang/System.out:Ljava/io/PrintStream;
    10: ldc           #4 // String Sync Block
    12: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    15: aload_1
    16: monitorexit            // release lock
    17: goto          25
    20: aload_1
    21: monitorexit            // release on exception
    22: aload_2
    23: athrow
    25: return
   Exception table:
        from    to  target type
            7    17    20   any
           20    22    20   any

Key instruction analysis: monitorenter: attempts to acquire the monitor of the lock object; on success the monitor counter increments. monitorexit: releases the monitor; when the counter reaches zero, waiting threads are awakened.

Exception handling: the JVM automatically inserts a second monitorexit to ensure the lock is released even if an exception occurs.

2. Bytecode for synchronized methods

When synchronized is used on a method, the JVM marks the method with the ACC_SYNCHRONIZED flag. The lock object is this for instance methods.

Example code:

public class SyncDemo {
    public synchronized void syncMethod() {
        System.out.println("Sync Method");
    }
}

Decompiled bytecode:

public synchronized void syncMethod();
   descriptor: ()V
   flags: ACC_PUBLIC, ACC_SYNCHRONIZED
   Code:
     0: getstatic     #3 // Field java/lang/System.out:Ljava/io/PrintStream;
     3: ldc           #4 // String Sync Method
     5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     8: return

Key flag analysis: ACC_SYNCHRONIZED: the JVM automatically acquires the monitor of the current instance ( this) before method execution and releases it afterward.

3. Bytecode for static synchronized methods

Static methods lock on the Class object. The ACC_STATIC flag indicates a static method.

Example code:

public class SyncDemo {
    public static synchronized void syncStatic() {
        System.out.println("Sync Static");
    }
}

Decompiled bytecode:

public static synchronized void syncStatic();
   descriptor: ()V
   flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
   Code:
     0: getstatic     #3 // Field java/lang/System.out:Ljava/io/PrintStream;
     3: ldc           #4 // String Sync Static
     5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     8: return

Key flag analysis: ACC_STATIC: indicates a static method; the lock object is SyncDemo.class. ACC_SYNCHRONIZED: JVM manages lock acquisition and release just like for instance methods.

Bytecode Implementation of CAS

CAS (Compare‑And‑Swap) provides lock‑free concurrency. It relies on JNI calls and hardware instructions such as cmpxchgl. Java’s atomic classes (e.g., AtomicInteger) wrap CAS operations.

1. CAS operation bytecode

Using AtomicInteger.compareAndSet as an example, the underlying call is Unsafe.compareAndSetInt, which eventually invokes native C++ code via JNI.

Example code:

public class CASDemo {
    private AtomicInteger atomicInt = new AtomicInteger(0);

    public boolean compareAndSet(int expected, int newValue) {
        return atomicInt.compareAndSet(expected, newValue);
    }
}

Decompiled bytecode:

public boolean compareAndSet(int, int);
   descriptor: (II)Z
   flags: ACC_PUBLIC
   Code:
     0: aload_0
     1: getfield      #2 // Field atomicInt:Ljava/util/concurrent/atomic/AtomicInteger;
     4: iload_1
     5: iload_2
     6: invokevirtual #3 // Method java/util/concurrent/atomic/AtomicInteger.compareAndSet:(II)Z
     9: ireturn

Key instruction analysis: invokevirtual: calls AtomicInteger.compareAndSet, which internally uses Unsafe.compareAndSetInt via JNI.

2. Unsafe.compareAndSetInt implementation

The native method Unsafe.compareAndSetInt invokes C++ code that uses the hardware cmpxchgl instruction to perform an atomic compare‑and‑swap.

C++ pseudo‑code (OpenJDK source):

bool compareAndSetInt(JNIEnv* env, jobject obj, jlong offset, jint expected, jint newValue) {
    // Obtain the address of the object's field
    volatile jint* address = (volatile jint*)((char*)obj + offset);
    // Use the cmpxchgl instruction to compare and swap
    return Atomic::compare_and_swap(newValue, address, expected);
}

Hardware instruction analysis: cmpxchgl (x86): compares the value in a register with the memory location; if equal, it writes the new value, otherwise it loads the memory value into the register.

Bytecode Correlation with Lock Upgrade Mechanism

The JVM records lock state in the object header’s Mark Word. At runtime the lock can be upgraded from no‑lock → biased lock → lightweight lock → heavyweight lock. Although bytecode does not directly show the upgrade, the following aspects are related.

1. Mark Word in the object header

No‑lock state: stores the object’s hash code.

Biased lock state: stores thread ID and bias timestamp.

Lightweight lock state: stores a pointer to a LockRecord in the thread’s stack.

Heavyweight lock state: stores a pointer to a Monitor object.

2. Triggers for lock upgrade

No‑lock → biased lock: the first thread entering a synchronized block uses CAS to set the biased lock flag.

Biased lock → lightweight lock: a second thread attempts to acquire the lock, causing the biased lock to be revoked and the JVM to try a lightweight lock via spinning and CAS.

Lightweight lock → heavyweight lock: if spinning fails, the JVM escalates to a heavyweight lock via a system call.

Summary

synchronized

Code blocks: implemented with monitorenter and monitorexit, relying on the monitor mechanism.

Methods: implemented with the ACC_SYNCHRONIZED flag; the lock object is this for instance methods or the Class object for static methods.

CAS

Atomic operation: performed via Unsafe.compareAndSetInt, which uses JNI and hardware instructions.

Performance advantage: avoids thread blocking and reduces context‑switch overhead.

Lock Upgrade

Dynamic optimization: the JVM adjusts the lock type based on contention, progressing from no‑lock to heavyweight lock.

Object header ( Mark Word) records the lock state and is the core data structure for lock upgrades.

Understanding bytecode instructions and the JVM’s internal mechanisms enables developers to write more efficient concurrent code and optimize performance bottlenecks.

JavaJVMconcurrencybytecodeCASsynchronized
Cognitive Technology Team
Written by

Cognitive Technology Team

Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.