Why Use Locks? Deep Dive into Java Synchronization, Volatile, and AQS

This article explains why locks are needed in concurrent programming, explores the fundamentals of volatile and synchronized, details monitor mechanisms, lock optimizations like biased and lightweight locks, compares CAS and AQS, and illustrates Java lock implementations with diagrams and code examples.

21CTO
21CTO
21CTO
Why Use Locks? Deep Dive into Java Synchronization, Volatile, and AQS

Why Use Locks?

Locks are used to solve dirty reads and data inconsistency caused by concurrent operations.

Basic Principles of Lock Implementation

volatile

Java allows threads to access shared variables; to ensure accurate and consistent updates, threads should acquire exclusive locks. The volatile keyword provides visibility of shared variables across threads, making it more convenient than locks in some cases.

When a thread modifies a volatile variable, other threads can see the updated value.

Conclusion: Proper use of volatile can be cheaper than synchronized because it avoids thread context switches.

synchronized

synchronized

achieves synchronization through a locking mechanism.

Every Java object can serve as a lock. There are three forms:

For ordinary synchronized methods, the lock is the current instance.

For static synchronized methods, the lock is the Class object.

For synchronized blocks, the lock is the object specified in the parentheses.

A thread must acquire the lock before entering a synchronized block and must release it upon exit or exception.

synchronized implementation principle

synchronized

is based on a Monitor.

The Monitor supports two aspects of thread synchronization:

Mutual exclusion

Cooperation

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

Monitor workflow

Thread enters a synchronized method.

To execute the critical section, the thread must acquire the Monitor lock; if successful, it becomes the owner.

The owning thread can call wait() to enter the wait set and release the monitor.

Other threads call 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.

synchronized concrete implementation

Synchronization blocks are implemented with the monitorenter and monitorexit bytecode instructions; synchronized methods use the ACC_SYNCHRONIZED flag.

Lock storage

Lock metadata is stored in the Mark Word of a Java object header.

Lock optimizations

Since Java SE 1.6, the JVM introduces biased locks and lightweight locks to reduce lock acquisition and release overhead.

Four lock states exist: no lock, biased lock, lightweight lock, and heavyweight lock. Locks can be upgraded but not downgraded.

Biased lock

Introduced to reduce overhead when there is no lock contention.

Lightweight lock

Suitable for scenarios where threads alternate executing synchronized blocks.

Lock coarsening

Combines adjacent lock/unlock operations into a larger lock region.

Lock elimination

The JIT compiler removes locks that are proven to have no shared data contention.

Adaptive spinning

Spin duration is adjusted based on previous spin success and the state of the lock owner.

CAS (Compare-And-Set)

CAS is an atomic operation that compares a memory location's value and, if equal, updates it to a new value, ensuring updates are based on the latest information.

In the JVM, CAS is implemented using the processor's CMPXCHG instruction.

Advantages: low overhead when contention is low.

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

Java Lock Implementations

AbstractQueuedSynchronizer (AQS)

AQS provides a framework for building locks and other synchronizers.

It uses an int field to represent synchronization state and a FIFO double-ended queue to manage threads waiting for the lock.

Threads that fail to acquire the lock create a node and append it to the queue tail; the queue follows FIFO order.

Exclusive vs Shared locks

Exclusive lock: only one thread can hold the lock (e.g., ReentrantLock).

Shared lock: multiple threads can hold the lock simultaneously (e.g., CountDownLatch).

Practical Example: ConcurrentHashMap

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

Conclusion: By using segmented locks, ConcurrentHashMap achieves high concurrency while maintaining thread safety.

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.

JavaconcurrencySynchronizationCASvolatileLocksAQS
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

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.