Why Use Locks in Java? Understanding volatile, synchronized, and CAS
This article explains why Java uses locks to prevent dirty reads and data inconsistency, compares volatile and synchronized mechanisms, details monitor-based lock implementation, lock optimizations like biased and lightweight locks, introduces CAS and AbstractQueuedSynchronizer, and illustrates these concepts with code examples and diagrams.
1. Why use locks?
Locks are used to solve dirty reads and data inconsistency caused by 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 volatile, which in some cases is more convenient than locks. volatile guarantees visibility of shared variables in multi‑processor development; when one thread modifies a shared variable, another thread can read the new value.
Conclusion: When used correctly, volatile has lower execution cost than synchronized because it does not trigger thread context switches.
2.2 synchronized
synchronized achieves synchronization through a lock mechanism.
In Java, every object can serve as a lock. There are three forms:
For a normal synchronized method, the lock is the current instance object.
For a static synchronized method, the lock is the Class object of the current class.
For a synchronized block, the lock is the object specified in the parentheses.
When a thread attempts to execute a synchronized block, it must first obtain the lock and must release it upon exiting or throwing an exception.
2.2.1 synchronized implementation principle
synchronized is based on Monitor to achieve synchronization.
Monitor supports thread synchronization in two aspects:
Mutual exclusion execution
Cooperation
1. Java uses object locks (obtained via synchronized) to guarantee mutual exclusion on shared data sets.
2. notify/notifyAll/wait methods are used to coordinate work between different threads.
3. Both Class and Object are associated with a Monitor.
2.2.2 Details of synchronized implementation
1. Synchronized code blocks are explicitly implemented using monitorenter and monitorexit instructions.
2. Synchronized methods use the ACC_SYNCHRONIZED flag for implicit implementation.
Example implementation:
public
class
SynchronizedTest
{
public
synchronized
void
method1(){
System.out.println("Hello World!");
}
public
void
method2(){
synchronized (this){
System.out.println("Hello World!");
}
}
}Javap compiled bytecode shows 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 tries to acquire the monitor according to these rules:
If the monitor entry count is 0, the thread can enter and the count becomes 1; the thread becomes the monitor owner.
If the current thread already owns the monitor and re‑enters, the entry count increments; the lock is re‑entrant.
If the monitor is owned by another thread, the current thread blocks until the entry count returns to 0.
monitorexit
Only the thread that owns the monitor can execute monitorexit. Each execution decrements the entry count; when it reaches 0, the thread releases the monitor, allowing other blocked threads to attempt acquisition.
2.2.3 Where the lock is stored
The lock mark is stored in the Mark Word of the Java object header.
2.2.4 Lock optimizations
Since Java SE 1.6, biased locks and lightweight locks were introduced to reduce the performance cost of acquiring and releasing locks.
Four lock states exist: no lock, biased lock, lightweight lock, heavyweight lock, which upgrade with contention but never downgrade.
Biased lock
In the absence of lock competition, biased lock reduces lock overhead.
Lightweight lock
Lightweight lock is suitable for scenarios where threads alternately execute synchronized blocks.
Lock coarsening : merging multiple 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 : the spin duration is adjusted based on previous lock acquisition success and the state of the lock owner.
2.2.5 Pros and cons of locks
2.3 CAS
CAS (Compare And Swap) is an atomic operation that compares a memory location's value and, if equal, updates it to a new value, ensuring the new value is based on the latest information.
1. CAS has low overhead when contention is low.
2. In the JVM, CAS is implemented using the processor's CMPXCHG instruction.
Advantages:
Low system overhead when contention is low.
Disadvantages:
Long loop time incurs high overhead.
ABA problem.
Only guarantees atomicity for a single shared variable.
3. Java lock implementations
3.1 AbstractQueuedSynchronizer (AQS)
AQS is a foundational framework for building locks and other synchronizers.
3.1.1 It uses an int field to represent synchronization state.
3.1.2 It uses an internal FIFO double‑ended queue to manage threads waiting for the lock.
The synchronizer maintains head and tail nodes; threads that fail to acquire the lock create a node and safely add it to the tail via compareAndSetTail. The queue follows FIFO order, and the head node holds the lock.
Threads that fail to acquire the lock create a node and set it as the tail node.
The head node thread, upon releasing the lock, wakes up its successor, which then becomes the new head after acquiring the lock.
3.1.3 Exclusive vs. shared lock acquisition
Exclusive: only one thread can acquire the lock (e.g., ReentrantLock). Shared: multiple threads can acquire the lock simultaneously (e.g., CountDownLatch).
Exclusive
Each node spins to check if its predecessor is the head; if so, it attempts to acquire the lock.
Exclusive lock acquisition flow diagram.
Shared
Difference between shared and exclusive locks.
4. Lock usage examples
4.1 ConcurrentHashMap implementation (Java 1.7)
ConcurrentHashMap uses segment locking: data is divided into segments, each protected by its own lock, allowing threads to access different segments concurrently.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
