Locks
Locks are more sophisticated than synchronized blocks or methods.
ReentrantLock
ReentrantLock is reentrant, meaning that a thread can lock the same
lock multiple times. This is different from the synchronized keyword,
which would cause a thread to deadlock if it tried to re-enter a
synchronized block it already holds. It's useful when it's not easy to
keep track of whether you've already acquired a lock.
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private static final ReentrantLock lock = new ReentrantLock();
private static int result;
public static void fib(int num) {
System.out.println("lock " + num);
lock.lock();
if (num == 1 || num == 2) {
result += 1;
} else {
fib(num - 1);
fib(num - 2);
}
System.out.println("unlock " + num);
lock.unlock();
}
public static void main(String[] args) {
result = 0;
fib(5);
System.out.println(result);
}
}
Expected output:
lock 5
lock 4
lock 3
lock 2
unlock 2
lock 1
unlock 1
unlock 3
lock 2
unlock 2
unlock 4
lock 3
lock 2
unlock 2
lock 1
unlock 1
unlock 3
unlock 5
5
ReadWriteLock
ReadWriteLock allows multiple threads to acquire read lock concurrently but
ensures that only one thread can write at a time.
There is also ReentrantReadWriteLock.
ReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.readLock().lock();
rwLock.readLock().unlock();
StampedLock
StampedLock provides optimistic read lock beside read and write lock.
Optimistic Read Lock tryOptimisticRead() Allows threads to
optimistically read data without blocking, and later verify whether
the read was successful or invalidated by a write.
This reduces contention and improves throughput when writes are infrequent.
import java.util.concurrent.locks.StampedLock;
class Counter {
private int count = 0;
private final StampedLock lock = new StampedLock();
public void increment() {
long stamp = lock.writeLock();
count++;
lock.unlockWrite(stamp);
System.out.println("incremented: " + count);
}
public int getCount() {
long stamp = lock.tryOptimisticRead();
int currentCount = count;
if (!lock.validate(stamp)) {
System.out.println("invalid read, retry");
stamp = lock.readLock();
currentCount = count;
lock.unlockRead(stamp);
}
return currentCount;
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
counter.increment();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Read Count: " + counter.getCount());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();
}
}
Expected output:
incremented: 1
Read Count: 1
incremented: 2
Read Count: 2
incremented: 3
invalid read, retry
Read Count: 3
Read Count: 3
incremented: 4
Read Count: 4
incremented: 5