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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
