Multithreading Revealed Part 3: Types of Locks for Multithreading in Java
Saksham Kapoor
MTS @ Oracle | Java, Spring, JavaScript, React, Docker , Kubernetes, OCI, Ruby
In multithreading, locks are essential to ensure that only one thread accesses a critical section of code at a time, preventing data corruption and ensuring thread safety. Java provides several types of locks with different features and use cases. Understanding these locks helps you choose the right one for your specific scenario.
1. Intrinsic Locks (Monitor Locks)
Every object in Java has an intrinsic lock (also known as a monitor lock) associated with it. When a thread enters a synchronized method or block, it acquires the intrinsic lock for that object.
public synchronized void synchronizedMethod() {
// Critical section
}
public void method() {
synchronized(this) {
// Critical section
}
}
Intrinsic locks are simple to use but come with limitations, such as no timed lock waits or interruptible lock waits.
2. ReentrantLock
ReentrantLock is part of the java.util.concurrent.locks package and provides more advanced locking features compared to intrinsic locks. It allows a thread to re-acquire the lock if it already holds it, hence the term reentrant.
领英推荐
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final Lock lock = new ReentrantLock();
public void performTask() {
lock.lock();
try {
// Critical section
} finally {
lock.unlock();
}
}
}
3. ReentrantReadWriteLock
ReentrantReadWriteLock improves performance in scenarios where read operations are more frequent than write operations. It provides separate locks for reading and writing, allowing multiple threads to read simultaneously while ensuring exclusive access for write operations.
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockExample {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
private int data;
public void writeData(int value) {
writeLock.lock();
try {
data = value;
} finally {
writeLock.unlock();
}
}
public int readData() {
readLock.lock();
try {
return data;
} finally {
readLock.unlock();
}
}
}
4. Semaphore
A Semaphore controls access to a resource that allows multiple permits, limiting the number of concurrent accesses.
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private final Semaphore semaphore = new Semaphore(3);
public void accessResource() {
try {
semaphore.acquire();
// Access resource
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
Semaphores are useful when you need to limit the number of threads accessing a particular resource.
Conclusion
Java offers a variety of locks to handle different multithreading scenarios, from simple intrinsic locks to more advanced ReentrantLock, ReentrantReadWriteLock, and Semaphore. Understanding these locks and their use cases can help you design more efficient and thread-safe applications.
Stay connected on LinkedIn for more articles on Java and other programming topics. Feel free to share your experiences, ask questions, and contribute to the discussion.