A lock is a synchronization mechanism that allows only one thread to access a shared object or class at a given time. When a thread acquires a lock, other threads attempting to access the same resource must wait until the lock is released.
- Only one thread can hold the lock at a time; others wait until it’s released.
- The Lock ensures mutual exclusion for accessing shared resources.
- Threads use lock.lock() to acquire and lock.unlock() to release after completion.
LockTypes of Locks in Java
Locks in Java can be divided into two categories:
1. Intrinsic Locks
These are the locks automatically provided by Java’s synchronized keyword.
1.1 Object Locks
An object lock is acquired when a thread enters a synchronized instance method or block. It ensures that only one thread can access an object’s synchronized methods or blocks at a time.
Java
public class ObjectLevelLock{
public synchronized void display()
{
System.out.println(
Thread.currentThread().getName()
+ " acquired object-level lock.");
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
}
}
public static void main(String[] args)
{
ObjectLevelLock obj
= new ObjectLevelLock();
Thread t1 = new Thread(obj::display, "Thread-1");
Thread t2 = new Thread(obj::display, "Thread-2");
t1.start();
t2.start();
}
}
Output:
outputExplanation: The method display() is synchronized, so each thread must acquire the object lock on obj before executing it. Since both t1 and t2 use the same object, only one thread can run the method at a time — the other waits until the lock is released.
1.2 Class-Level Lock
A Class-level lock is used to synchronize static methods or blocks. It locks the Class object (ClassName.class), so threads accessing the same class (even with different instances) must wait.
Java
public class ClassLevelLock{
public static synchronized void show(){
System.out.println(Thread.currentThread().getName()
+ " acquired class-level lock.");
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
}
}
public static void main(String[] args){
Thread t1 = new Thread(ClassLevelLock::show,
"Thread-1");
Thread t2 = new Thread(ClassLevelLock::show,
"Thread-2");
t1.start();
t2.start();
}
}
Output:
outputExplanation: Both threads try to access the same class-level synchronized method, so one must wait until the other releases the class-level lock. Even if new instances are created, they share the same class-level lock.
2. Explicit Locks (from java.util.concurrent.locks)
Unlike synchronized, explicit locks must be manually acquired and released, giving developers more flexibility and control.
2.1. ReentrantLock
A ReentrantLock provides similar locking behavior as synchronized, but with more flexibility. The same thread can re-acquire the lock multiple times.
Java
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void task()
{
lock.lock();
try {
System.out.println(
Thread.currentThread().getName()
+ " acquired ReentrantLock.");
}
finally {
lock.unlock();
}
}
public static void main(String[] args)
{
ReentrantLockExample obj
= new ReentrantLockExample();
new Thread(obj::task, "Thread-1").start();
new Thread(obj::task, "Thread-2").start();
}
}
Output:
outputExplanation: Here, ReentrantLock ensures that only one thread executes the critical section at a time. It must always be released in a finally block to avoid deadlocks.
2.2. ReentrantReadWriteLock
ReentrantReadWriteLock Used when multiple threads read frequently but only a few write. It allows multiple readers or one writer at a time.
Java
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample{
private final ReentrantReadWriteLock rwLock
= new ReentrantReadWriteLock();
private int data = 0;
public void write(){
rwLock.writeLock().lock();
try{
data++;
System.out.println(
Thread.currentThread().getName()
+ " wrote: " + data);
}
finally{
rwLock.writeLock().unlock();
}
}
public void read(){
rwLock.readLock().lock();
try {
System.out.println(
Thread.currentThread().getName()
+ " read: " + data);
}
finally {
rwLock.readLock().unlock();
}
}
public static void main(String[] args){
ReadWriteLockExample obj
= new ReadWriteLockExample();
new Thread(obj::write, "Writer").start();
new Thread(obj::read, "Reader-1").start();
new Thread(obj::read, "Reader-2").start();
}
}
Output:
outputExplanation: This example allows multiple readers to access data simultaneously, but only one writer at a time. Writers get exclusive access to prevent modification conflicts.
Commonly Used Lock Methods
| Method | Description |
|---|
| lock() | Acquires the lock; blocks if not available. |
| unlock() | Releases the lock. |
| tryLock() | Attempts to acquire the lock without waiting. |
| lockInterruptibly() | Acquires the lock unless interrupted. |
| isLocked() | Checks if the lock is currently held. |