Fundamentals 12 min read

Understanding Java Locks: volatile, synchronized, Monitor, CAS, and AQS

This article explains why locks are needed in Java, describes the fundamentals of volatile and synchronized, details monitor-based synchronization, explores lock optimizations such as biased and lightweight locks, and covers CAS and the AbstractQueuedSynchronizer framework for building concurrent structures.

Java Captain
Java Captain
Java Captain
Understanding Java Locks: volatile, synchronized, Monitor, CAS, and AQS

1. Why Use Locks?

Locks are employed to prevent dirty reads and data inconsistency that arise from concurrent operations.

2. Basic Principles of Lock Implementation

2.1 volatile

The Java language allows threads to access shared variables; to ensure accurate and consistent updates, threads should obtain exclusive access via a lock. Java provides the volatile keyword, which in some cases is more convenient than a lock. volatile guarantees the "visibility" of shared variables on multiprocessor systems, meaning that when one thread modifies a variable, other threads can see the updated value.

Conclusion: When used appropriately, a volatile variable incurs lower overhead than synchronized because it avoids thread context switches and scheduling.

2.2 synchronized

synchronized achieves synchronization through a locking mechanism.

In Java, every object can serve as a lock. The lock can appear in three forms:

For a regular synchronized method, the lock is the current instance object.

For a static synchronized method, the lock is the Class object of the class.

For a synchronized block, the lock is the object specified inside the parentheses.

When a thread attempts to enter a synchronized block, it must first acquire the lock and must release it when exiting or throwing an exception.

2.2.1 Implementation Principle of synchronized

synchronized is implemented based on a Monitor.

The Monitor supports thread synchronization in two ways:

Mutual exclusion execution.

Cooperation between threads.

Java uses object locks (obtained via synchronized) to guarantee mutual exclusion on shared data, and uses notify / notifyAll / wait to coordinate thread interactions. Both Class and Object are associated with a Monitor.

The typical workflow is:

A thread enters a synchronized method.

It must acquire the Monitor lock; if successful, it becomes the owner of that Monitor.

The owning thread can call wait(), entering the wait set and releasing the lock.

Other threads invoke notify() or notifyAll() to wake waiting threads, which must reacquire the Monitor before proceeding.

When the synchronized method finishes, the thread exits the critical section and releases the Monitor.

2.2.2 Concrete Implementation of synchronized

1. Synchronized blocks are compiled into explicit monitorenter and monitorexit bytecode instructions.

2. Synchronized methods are implicitly marked with the ACC_SYNCHRONIZED flag.

Example implementation:

public class SynchronizedTest {

    public synchronized void method1() {
        System.out.println("Hello World!");
    }

    public void method2() {
        synchronized (this) {
            System.out.println("Hello World!");
        }
    }
}

After compilation, the javap output shows the monitorenter and monitorexit instructions. The lock acquisition rules are:

If the monitor entry count is 0, the thread can acquire the monitor and the count becomes 1.

If the current thread already owns the monitor, the entry count is incremented, making the lock re‑entrant.

If another thread owns the monitor, the requesting thread blocks until the count returns to 0.

Only the thread that owns the monitor may execute monitorexit; each execution decrements the entry count, and when it reaches 0 the monitor is released.

2.2.3 Where Locks Are Stored

The lock flag is stored in the Mark Word of a Java object header.

2.2.4 Lock Optimizations

Since Java SE 1.6, the JVM introduces biased locks and lightweight locks to reduce the cost of acquiring and releasing locks.

The JVM defines four lock states (from low to high): no‑lock, biased lock, lightweight lock, and heavyweight lock. Locks can be upgraded but not downgraded.

Biased Lock

When there is no contention, a biased lock reduces the overhead of lock acquisition.

Lightweight Lock

Lightweight locks are suitable for scenarios where threads alternate execution of synchronized blocks.

Additional optimizations include lock coarsening (merging adjacent lock/unlock pairs into a larger region), lock elimination (JIT removes locks that are proven unnecessary), and adaptive spinning (the JVM adjusts spin‑wait duration based on previous lock acquisition success).

2.2.5 Comparison of Lock Advantages and Disadvantages

(Illustrative diagram omitted for brevity.)

2.3 CAS

CAS (Compare‑And‑Swap or Compare‑And‑Set) is an atomic operation that compares a memory location's value with an expected value and, if they match, updates it to a new value.

Advantages: low overhead when contention is small.

Disadvantages: high CPU cost under long spin loops, ABA problem, and it only guarantees atomicity for a single shared variable.

3. Lock Implementations in Java

3.1 AbstractQueuedSynchronizer (AQS)

AQS is a framework for building locks and other synchronizers.

3.1.1 State Representation

AQS uses an int field to represent the synchronization state.

3.1.2 FIFO Queue for Thread Ordering

Threads that fail to acquire the lock are enqueued in a FIFO double‑linked list. The head node holds the thread that currently owns the lock; releasing the lock wakes the successor node.

3.1.3 Exclusive vs. Shared Locks

Exclusive lock: only one thread can hold it at a time (e.g., ReentrantLock ). Shared lock: multiple threads can hold it concurrently (e.g., CountDownLatch ).

Exclusive Lock Acquisition

Each node spins, checking whether its predecessor is the head; if so, it attempts to acquire the lock.

Shared Lock Acquisition

The acquisition flow differs, allowing multiple threads to proceed simultaneously.

4. Practical Lock Usage Examples

4.1 Implementation and Use of ConcurrentHashMap

ConcurrentHashMap employs a segmented locking technique: the data is divided into segments, each protected by its own lock, allowing concurrent access to different segments.

Original source: jianshu.com/p/e674ee68fd3f

(Images illustrating class diagrams and data structures are omitted for brevity.)

Overall, understanding the various lock mechanisms—volatile, synchronized, monitor, CAS, and AQS—helps developers write efficient, thread‑safe Java code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaCASvolatileLocksAQSsynchronized
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

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.