Understanding Java Locks, Synchronization, and Concurrency Mechanisms
This article explains why locks are needed in Java, compares volatile and synchronized, describes monitor‑based synchronization, lock optimizations such as biased and lightweight locks, introduces CAS and the AbstractQueuedSynchronizer framework, and provides practical code examples and usage scenarios.
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
Java allows threads to access shared variables; to ensure accurate and consistent updates, threads should acquire an exclusive lock for the variable. Java provides the volatile keyword, which in some cases is more convenient than explicit locks. volatile guarantees visibility of shared variables in multi‑processor environments—when one thread modifies a variable, other threads can see the new value.
Conclusion: When used correctly, volatile is cheaper than synchronized because it avoids thread context switches and scheduling overhead.
2.2 synchronized
synchronized achieves synchronization through a lock mechanism.
In Java, every object can serve as a lock.
There are three forms of locking:
For a regular instance synchronized method, the lock is the current object instance.
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 execute a synchronized block, it must first acquire the lock; the lock is released when the thread exits the block or throws an exception.
2.2.1 How synchronized Works
synchronized is built on the concept of a monitor.
A monitor supports two aspects of thread coordination:
Mutual exclusion
Cooperation (wait/notify)
1. Java uses object monitors (acquired via synchronized) to guarantee mutual exclusion on shared data.
2. Methods wait, notify, and notifyAll are used for thread cooperation.
3. Both Class and Object instances are associated with a monitor.
Monitor operation flow:
A thread enters a synchronized method.
To continue executing the critical section, the thread must acquire the monitor lock; if successful, it becomes the owner.
The owning thread can call wait(), entering the wait set and releasing the monitor.
Other threads invoke notify() or notifyAll() to wake waiting threads, which must reacquire the monitor before proceeding.
When the synchronized method finishes, the thread releases the monitor.
Reference: IBM Java synchronized documentation
2.2.2 Concrete Implementation of synchronized
1. Synchronized blocks are compiled to explicit monitorenter and monitorexit bytecode instructions.
2. Synchronized methods are 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!");
}
}
}Compiled bytecode shows the use of monitorenter and monitorexit instructions.
monitorenter
Each object has a monitor; only one thread can own it at a time. When a thread executes monitorenter, it attempts to acquire the monitor according to these rules:
If the monitor’s entry count is 0, the thread becomes the owner and the count is set to 1.
If the current thread already owns the monitor, the entry count is incremented (re‑entrant lock).
If another thread owns the monitor, the requesting thread blocks until the count returns to 0.
monitorexit
Only the owning thread may execute monitorexit. Each execution decrements the entry count; when it reaches 0, the monitor is released and other blocked threads may attempt acquisition.
2.2.3 Where Locks Are Stored
Lock metadata is stored in the Mark Word of a Java object header.
Illustrations of 32‑bit and 64‑bit JVM Mark Word structures and state transitions are shown.
2.2.4 Lock Optimizations (Java 6+)
Java 6 introduced biased locks and lightweight locks to reduce the overhead of acquiring and releasing locks.
Locks progress through four states (from low to high cost): no‑lock, biased lock, lightweight lock, heavyweight lock. The state upgrades with contention but never downgrades.
Biased Lock
When there is no contention, a biased lock avoids the cost of lock acquisition.
Lightweight Lock
Lightweight locks are suitable for scenarios where threads alternate execution of synchronized blocks.
Additional JVM optimizations include lock coarsening, lock elimination, and adaptive spinning.
2.2.5 Comparison of Lock Advantages and Disadvantages
2.3 CAS (Compare‑And‑Swap)
CAS (CompareAndSwap or CompareAndSet) is an atomic operation that compares a memory location’s value with an expected value and, only if they match, updates it to a new value.
In Java, CAS is implemented using the processor’s CMPXCHG instruction.
Advantages: low overhead when contention is low.
Disadvantages: high cost under heavy contention, ABA problem, and it only guarantees atomicity for a single variable.
3. Java Lock Implementations
3.1 AbstractQueuedSynchronizer (AQS)
AQS is a framework for building locks and other synchronizers.
3.1.1 Synchronization State
AQS uses an int field to represent the synchronization state.
3.1.2 FIFO Queue for Thread Contention
Threads that fail to acquire the lock are enqueued in a doubly‑linked FIFO queue. The head node holds the lock; subsequent nodes wait for their turn.
When the head node releases the lock, it wakes its successor, which then becomes the new head.
3.1.3 Exclusive vs. Shared Locks
Exclusive lock: only one thread can hold it (e.g., ReentrantLock ). Shared lock: multiple threads can hold it simultaneously (e.g., CountDownLatch ).
Exclusive Lock Acquisition
Each node spins, checking if its predecessor is the head; if so, it attempts to acquire the lock.
Shared Lock Acquisition
Differences between shared and exclusive modes are illustrated.
4. Lock Usage Examples
4.1 ConcurrentHashMap Implementation
Conclusion: ConcurrentHashMap uses lock striping; the data structure is divided into segments, each protected by its own lock, allowing concurrent access to different segments.
(End)
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.
