4.2.4 Locks Explained
Locks in Java are synchronization mechanisms that provide more flexibility and control over traditional synchronized blocks. They are part of the java.util.concurrent.locks
package and offer advanced features like reentrant locking, timed locking, and interruptible locking.
Key Concepts
1. ReentrantLock
The ReentrantLock
class is a mutual exclusion lock with the same basic behavior as the implicit monitor lock accessed using synchronized
methods and blocks. However, it has additional features like fairness and the ability to interrupt threads waiting for the lock.
Example
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Counter { private int count = 0; private Lock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } public int getCount() { return count; } } public class Main { public static void main(String[] args) { Counter counter = new Counter(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(counter.getCount()); // Output: 2000 } }
2. ReadWriteLock
The ReadWriteLock
interface and its implementation ReentrantReadWriteLock
provide a way to differentiate between read-only and write operations. Multiple threads can read the data simultaneously, but only one thread can write at a time, and no reads are allowed while writing.
Example
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; class Data { private int value; private ReadWriteLock lock = new ReentrantReadWriteLock(); public void write(int newValue) { lock.writeLock().lock(); try { value = newValue; } finally { lock.writeLock().unlock(); } } public int read() { lock.readLock().lock(); try { return value; } finally { lock.readLock().unlock(); } } } public class Main { public static void main(String[] args) { Data data = new Data(); data.write(10); System.out.println(data.read()); // Output: 10 } }
3. StampedLock
The StampedLock
class provides a more advanced locking mechanism that supports optimistic reads. It allows for a form of non-blocking read access that can be validated later, providing better performance in scenarios with frequent reads and infrequent writes.
Example
import java.util.concurrent.locks.StampedLock; class Point { private double x, y; private final StampedLock sl = new StampedLock(); public void move(double deltaX, double deltaY) { long stamp = sl.writeLock(); try { x += deltaX; y += deltaY; } finally { sl.unlockWrite(stamp); } } public double distanceFromOrigin() { long stamp = sl.tryOptimisticRead(); double currentX = x, currentY = y; if (!sl.validate(stamp)) { stamp = sl.readLock(); try { currentX = x; currentY = y; } finally { sl.unlockRead(stamp); } } return Math.sqrt(currentX * currentX + currentY * currentY); } } public class Main { public static void main(String[] args) { Point point = new Point(); point.move(3, 4); System.out.println(point.distanceFromOrigin()); // Output: 5.0 } }
Examples and Analogies
Think of ReentrantLock
as a key to a room. Only one person (thread) can hold the key at a time, ensuring that only one person can enter the room. ReadWriteLock
is like a library where multiple people can read books (read operations) simultaneously, but only one person can write (update) a book at a time. StampedLock
is like a ticket system where readers can get a temporary pass (optimistic read) and validate it later, ensuring that they have the most up-to-date information.
By mastering these lock mechanisms, you can develop more efficient and scalable Java applications that handle concurrent operations effectively.