Synchronization is primarily achieved using the
synchronized keyword, which relies on an internal locking mechanism known as intrinsic locks or monitor locks.
The synchronized Keyword
Thesynchronized keyword is used to control access to a block of code or method so that only one thread can execute it at a time for a given object.
When a thread enters a synchronized section, it acquires a lock. Other threads attempting to enter the same section must wait until the lock is released.
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
In this example, only one thread can execute the increment() method at a time for the same Counter object.
How synchronized Works
Every object in Java has an associated intrinsic lock. When a thread enters a synchronized method or block, it acquires this lock.- If the lock is available โ thread proceeds
- If the lock is held โ thread enters BLOCKED state
- Once the thread exits the synchronized section, the lock is released automatically.
Method-Level Synchronization
When you declare a method assynchronized, the lock is acquired on the entire object (this).
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
Characteristics:
- Locks the entire method- Simple and easy to use
- May reduce performance if overused
Block-Level Synchronization
Instead of locking the entire method, you can synchronize only a specific block of code. This provides more control and better performance.class Counter {
private int count = 0;
public void increment() {
synchronized (this) {
count++;
}
}
}
Here, only the critical section is locked, not the entire method.
Advantages:
- Better performance (smaller locked region)- More fine-grained control
- Allows different locks for different resources
Example with custom lock object:
Using a private lock object instead ofthis improves encapsulation, because no external code can access or interfere with this lock. This prevents unintended blocking or synchronization issues caused by outside components.
class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
}
Method vs Block-Level Synchronization
Method-Level:
- Locks entire method- Easier to implement
- Less flexible
Block-Level:
- Locks only specific code- More efficient
- Preferred in real-world applications
In practice, block-level synchronization is generally preferred for better performance and scalability.
Intrinsic Locks (Monitor Locks)
Java uses intrinsic locks, also known as monitor locks, to implement synchronization.Every object has a built-in lock that can be used for synchronization. When a thread enters a synchronized block or method, it acquires the object's monitor lock.
Object object = new Object();
synchronized (object) {
// thread holds object's monitor lock
System.out.println("Inside synchronized block");
}
Key Properties of Intrinsic Locks:
- Each object has one monitor lock- Only one thread can hold the lock at a time
- Locks are reentrant (same thread can acquire multiple times)
- Automatically released when exiting synchronized block
Reentrancy Example
class Example {
public synchronized void methodA() {
methodB();
}
public synchronized void methodB() {
System.out.println("Reentrant lock acquired");
}
}
Here, the same thread can enter methodB() even though it already holds the lock from methodA().
Synchronization is a fundamental concept in Java concurrency that ensures safe access to shared resources. By using the
synchronized keyword, developers can protect critical sections and prevent race conditions.
Understanding the difference between method-level and block-level synchronization, along with the role of intrinsic locks, is essential for writing efficient and scalable multithreaded applications.
In the next chapter, we will dive deeper into volatile keyword and explore how memory visibility plays a critical role in concurrent programming.
Join the discussion